blob: ed2255ef3fee8bec03d5f82da4b49735d7093c21 [file] [log] [blame]
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001/* vi: set sw=4 ts=4: */
2/*
3 * DHCPv6 client.
4 *
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +02005 * WARNING: THIS CODE IS INCOMPLETE.
Denys Vlasenko9ba75042011-11-07 15:55:39 +01006 *
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +02007 * Copyright (C) 2011-2017 Denys Vlasenko.
Denys Vlasenko9ba75042011-11-07 15:55:39 +01008 *
9 * Licensed under GPLv2, see file LICENSE in this source tree.
10 */
Denys Vlasenko9ba75042011-11-07 15:55:39 +010011//config:config UDHCPC6
Denys Vlasenko68b653b2017-07-27 10:53:09 +020012//config: bool "udhcpc6"
Mike Frysinger3da46c82012-05-02 21:45:35 -040013//config: default n # not yet ready
14//config: depends on FEATURE_IPV6
15//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020016//config: udhcpc6 is a DHCPv6 client
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +020017//config:
18//config:config FEATURE_UDHCPC6_RFC3646
19//config: bool "Support RFC 3646 (DNS server and search list)"
20//config: default y
21//config: depends on UDHCPC6
22//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020023//config: List of DNS servers and domain search list can be requested with
24//config: "-O dns" and "-O search". If server gives these values,
25//config: they will be set in environment variables "dns" and "search".
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +020026//config:
27//config:config FEATURE_UDHCPC6_RFC4704
28//config: bool "Support RFC 4704 (Client FQDN)"
29//config: default y
30//config: depends on UDHCPC6
31//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020032//config: You can request FQDN to be given by server using "-O fqdn".
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +020033//config:
34//config:config FEATURE_UDHCPC6_RFC4833
35//config: bool "Support RFC 4833 (Timezones)"
36//config: default y
37//config: depends on UDHCPC6
38//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020039//config: You can request POSIX timezone with "-O tz" and timezone name
40//config: with "-O timezone".
Samuel Mendoza-Jonas23cbd7d2018-05-14 14:29:12 +100041//config:
42//config:config FEATURE_UDHCPC6_RFC5970
43//config: bool "Support RFC 5970 (Network Boot)"
44//config: default y
45//config: depends on UDHCPC6
46//config: help
47//config: You can request bootfile-url with "-O bootfile_url" and
48//config: bootfile-params with "-O bootfile_params".
Denys Vlasenko9ba75042011-11-07 15:55:39 +010049
50//applet:IF_UDHCPC6(APPLET(udhcpc6, BB_DIR_USR_BIN, BB_SUID_DROP))
51
Denys Vlasenko8cab6672012-04-20 14:48:00 +020052//kbuild:lib-$(CONFIG_UDHCPC6) += d6_dhcpc.o d6_packet.o d6_socket.o common.o socket.o signalpipe.o
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +020053//kbuild:lib-$(CONFIG_FEATURE_UDHCPC6_RFC3646) += domain_codec.o
54//kbuild:lib-$(CONFIG_FEATURE_UDHCPC6_RFC4704) += domain_codec.o
Denys Vlasenko9ba75042011-11-07 15:55:39 +010055
56#include <syslog.h>
57/* Override ENABLE_FEATURE_PIDFILE - ifupdown needs our pidfile to always exist */
58#define WANT_PIDFILE 1
59#include "common.h"
60#include "dhcpd.h"
61#include "dhcpc.h"
62#include "d6_common.h"
63
64#include <netinet/if_ether.h>
65#include <netpacket/packet.h>
66#include <linux/filter.h>
67
68/* "struct client_config_t client_config" is in bb_common_bufsiz1 */
69
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +020070static const struct dhcp_optflag d6_optflags[] = {
71#if ENABLE_FEATURE_UDHCPC6_RFC3646
72 { OPTION_6RD | OPTION_LIST | OPTION_REQ, D6_OPT_DNS_SERVERS },
73 { OPTION_DNS_STRING | OPTION_LIST | OPTION_REQ, D6_OPT_DOMAIN_LIST },
74#endif
75#if ENABLE_FEATURE_UDHCPC6_RFC4704
76 { OPTION_DNS_STRING, D6_OPT_CLIENT_FQDN },
77#endif
78#if ENABLE_FEATURE_UDHCPC6_RFC4833
79 { OPTION_STRING, D6_OPT_TZ_POSIX },
80 { OPTION_STRING, D6_OPT_TZ_NAME },
81#endif
Samuel Mendoza-Jonas23cbd7d2018-05-14 14:29:12 +100082#if ENABLE_FEATURE_UDHCPC6_RFC5970
83 { OPTION_STRING, D6_OPT_BOOT_URL },
84 { OPTION_STRING, D6_OPT_BOOT_PARAM },
85#endif
Samuel Mendoza-Jonas30f4d522018-05-14 14:29:13 +100086 { OPTION_STRING, 0xd1 }, /* DHCP_PXE_CONF_FILE */
87 { OPTION_STRING, 0xd2 }, /* DHCP_PXE_PATH_PREFIX */
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +020088 { 0, 0 }
89};
90/* Must match d6_optflags[] order */
91static const char d6_option_strings[] ALIGN1 =
92#if ENABLE_FEATURE_UDHCPC6_RFC3646
93 "dns" "\0" /* D6_OPT_DNS_SERVERS */
94 "search" "\0" /* D6_OPT_DOMAIN_LIST */
95#endif
96#if ENABLE_FEATURE_UDHCPC6_RFC4704
97 "fqdn" "\0" /* D6_OPT_CLIENT_FQDN */
98#endif
99#if ENABLE_FEATURE_UDHCPC6_RFC4833
100 "tz" "\0" /* D6_OPT_TZ_POSIX */
101 "timezone" "\0" /* D6_OPT_TZ_NAME */
102#endif
Samuel Mendoza-Jonas23cbd7d2018-05-14 14:29:12 +1000103#if ENABLE_FEATURE_UDHCPC6_RFC5970
104 "bootfile_url" "\0" /* D6_OPT_BOOT_URL */
105 "bootfile_param" "\0" /* D6_OPT_BOOT_PARAM */
106#endif
Samuel Mendoza-Jonas30f4d522018-05-14 14:29:13 +1000107 "pxeconffile" "\0" /* DHCP_PXE_CONF_FILE */
108 "pxepathprefix" "\0" /* DHCP_PXE_PATH_PREFIX */
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200109 "\0";
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100110
111#if ENABLE_LONG_OPTS
Denys Vlasenko7e21f042011-11-08 11:39:41 +0100112static const char udhcpc6_longopts[] ALIGN1 =
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100113 "interface\0" Required_argument "i"
114 "now\0" No_argument "n"
115 "pidfile\0" Required_argument "p"
116 "quit\0" No_argument "q"
117 "release\0" No_argument "R"
118 "request\0" Required_argument "r"
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100119 "requestprefix\0" No_argument "d"
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100120 "script\0" Required_argument "s"
121 "timeout\0" Required_argument "T"
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100122 "retries\0" Required_argument "t"
123 "tryagain\0" Required_argument "A"
124 "syslog\0" No_argument "S"
125 "request-option\0" Required_argument "O"
126 "no-default-options\0" No_argument "o"
127 "foreground\0" No_argument "f"
Denys Vlasenkoed820cc2017-05-08 15:11:02 +0200128 USE_FOR_MMU(
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100129 "background\0" No_argument "b"
Denys Vlasenkoed820cc2017-05-08 15:11:02 +0200130 )
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100131/// IF_FEATURE_UDHCPC_ARPING("arping\0" No_argument "a")
132 IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P")
133 ;
134#endif
135/* Must match getopt32 option string order */
136enum {
137 OPT_i = 1 << 0,
138 OPT_n = 1 << 1,
139 OPT_p = 1 << 2,
140 OPT_q = 1 << 3,
141 OPT_R = 1 << 4,
142 OPT_r = 1 << 5,
143 OPT_s = 1 << 6,
144 OPT_T = 1 << 7,
145 OPT_t = 1 << 8,
146 OPT_S = 1 << 9,
147 OPT_A = 1 << 10,
148 OPT_O = 1 << 11,
149 OPT_o = 1 << 12,
150 OPT_x = 1 << 13,
151 OPT_f = 1 << 14,
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100152 OPT_d = 1 << 15,
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100153/* The rest has variable bit positions, need to be clever */
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100154 OPTBIT_d = 15,
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100155 USE_FOR_MMU( OPTBIT_b,)
156 ///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
157 IF_FEATURE_UDHCP_PORT( OPTBIT_P,)
158 USE_FOR_MMU( OPT_b = 1 << OPTBIT_b,)
159 ///IF_FEATURE_UDHCPC_ARPING(OPT_a = 1 << OPTBIT_a,)
160 IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,)
161};
162
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200163#if ENABLE_FEATURE_UDHCPC6_RFC4704
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200164static const char opt_fqdn_req[] = {
165 (D6_OPT_CLIENT_FQDN >> 8), (D6_OPT_CLIENT_FQDN & 0xff),
Denys Vlasenko470bebe2017-06-27 18:31:08 +0200166 0, 2, /* optlen */
167 0, /* flags: */
168 /* S=0: server SHOULD NOT perform AAAA RR updates */
169 /* O=0: client MUST set this bit to 0 */
170 /* N=0: server SHOULD perform updates (PTR RR only in our case, since S=0) */
171 0 /* empty DNS-encoded name */
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200172};
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200173#endif
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100174
175/*** Utility functions ***/
176
177static void *d6_find_option(uint8_t *option, uint8_t *option_end, unsigned code)
178{
179 /* "length minus 4" */
180 int len_m4 = option_end - option - 4;
181 while (len_m4 >= 0) {
182 /* Next option's len is too big? */
Denys Vlasenko68c5b282011-11-07 16:21:24 +0100183 if (option[3] > len_m4)
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100184 return NULL; /* yes. bogus packet! */
185 /* So far we treat any opts with code >255
186 * or len >255 as bogus, and stop at once.
187 * This simplifies big-endian handling.
188 */
Denys Vlasenko68c5b282011-11-07 16:21:24 +0100189 if (option[0] != 0 || option[2] != 0)
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100190 return NULL;
191 /* Option seems to be valid */
192 /* Does its code match? */
Denys Vlasenko68c5b282011-11-07 16:21:24 +0100193 if (option[1] == code)
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100194 return option; /* yes! */
Denys Vlasenko68c5b282011-11-07 16:21:24 +0100195 len_m4 -= option[3] + 4;
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200196 option += option[3] + 4;
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100197 }
198 return NULL;
199}
200
201static void *d6_copy_option(uint8_t *option, uint8_t *option_end, unsigned code)
202{
203 uint8_t *opt = d6_find_option(option, option_end, code);
204 if (!opt)
205 return opt;
Ron Yorstond840c5d2015-07-19 23:05:20 +0200206 return xmemdup(opt, opt[3] + 4);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100207}
208
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100209
210/*** Script execution code ***/
211
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100212static char** new_env(void)
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100213{
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100214 client6_data.env_ptr = xrealloc_vector(client6_data.env_ptr, 3, client6_data.env_idx);
215 return &client6_data.env_ptr[client6_data.env_idx++];
216}
217
David Decotigny8f48fc02018-05-24 08:30:15 -0700218static char *string_option_to_env(const uint8_t *option,
219 const uint8_t *option_end)
Samuel Mendoza-Jonasbcdec1a2018-05-14 14:29:11 +1000220{
221 const char *ptr, *name = NULL;
222 unsigned val_len;
223 int i;
224
225 ptr = d6_option_strings;
226 i = 0;
227 while (*ptr) {
228 if (d6_optflags[i].code == option[1]) {
229 name = ptr;
230 goto found;
231 }
232 ptr += strlen(ptr) + 1;
233 i++;
234 }
235 bb_error_msg("can't find option name for 0x%x, skipping", option[1]);
236 return NULL;
237
238 found:
239 val_len = (option[2] << 8) | option[3];
240 if (val_len + &option[D6_OPT_DATA] > option_end) {
241 bb_error_msg("option data exceeds option length");
242 return NULL;
243 }
244 return xasprintf("%s=%.*s", name, val_len, (char*)option + 4);
245}
246
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100247/* put all the parameters into the environment */
David Decotigny8f48fc02018-05-24 08:30:15 -0700248static void option_to_env(const uint8_t *option, const uint8_t *option_end)
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100249{
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200250#if ENABLE_FEATURE_UDHCPC6_RFC3646
251 int addrs, option_offset;
252#endif
Denys Vlasenkoab030612017-03-27 22:49:12 +0200253 /* "length minus 4" */
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100254 int len_m4 = option_end - option - 4;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200255
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100256 while (len_m4 >= 0) {
257 uint32_t v32;
258 char ipv6str[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")];
259
260 if (option[0] != 0 || option[2] != 0)
261 break;
262
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200263 /* Check if option-length exceeds size of option */
264 if (option[3] > len_m4)
265 break;
266
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100267 switch (option[1]) {
268 //case D6_OPT_CLIENTID:
269 //case D6_OPT_SERVERID:
270 case D6_OPT_IA_NA:
271 case D6_OPT_IA_PD:
272 option_to_env(option + 16, option + 4 + option[3]);
273 break;
274 //case D6_OPT_IA_TA:
275 case D6_OPT_IAADDR:
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200276/* 0 1 2 3
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100277 * 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
278 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
279 * | OPTION_IAADDR | option-len |
280 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
281 * | |
282 * | IPv6 address |
283 * | |
284 * | |
285 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
286 * | preferred-lifetime |
287 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
288 * | valid-lifetime |
289 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
290 */
David Decotignya174c792018-05-24 08:30:16 -0700291 /* Make sure payload contains an address */
292 if (option[3] < 24)
293 break;
294
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100295 sprint_nip6(ipv6str, option + 4);
296 *new_env() = xasprintf("ipv6=%s", ipv6str);
297
298 move_from_unaligned32(v32, option + 4 + 16 + 4);
299 *new_env() = xasprintf("lease=%u", (unsigned)v32);
300 break;
301
302 //case D6_OPT_ORO:
303 //case D6_OPT_PREFERENCE:
304 //case D6_OPT_ELAPSED_TIME:
305 //case D6_OPT_RELAY_MSG:
306 //case D6_OPT_AUTH:
307 //case D6_OPT_UNICAST:
308 //case D6_OPT_STATUS_CODE:
309 //case D6_OPT_RAPID_COMMIT:
310 //case D6_OPT_USER_CLASS:
311 //case D6_OPT_VENDOR_CLASS:
312 //case D6_OPT_VENDOR_OPTS:
313 //case D6_OPT_INTERFACE_ID:
314 //case D6_OPT_RECONF_MSG:
315 //case D6_OPT_RECONF_ACCEPT:
316
317 case D6_OPT_IAPREFIX:
318/* 0 1 2 3
319 * 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
320 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
321 * | OPTION_IAPREFIX | option-length |
322 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
323 * | preferred-lifetime |
324 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
325 * | valid-lifetime |
326 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
327 * | prefix-length | |
328 * +-+-+-+-+-+-+-+-+ IPv6 prefix |
329 * | (16 octets) |
330 * | |
331 * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
332 * | |
333 * +-+-+-+-+-+-+-+-+
334 */
Denys Vlasenko688cb3b2018-01-16 16:00:13 +0100335 move_from_unaligned32(v32, option + 4 + 4);
336 *new_env() = xasprintf("ipv6prefix_lease=%u", (unsigned)v32);
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100337
Denys Vlasenko688cb3b2018-01-16 16:00:13 +0100338 sprint_nip6(ipv6str, option + 4 + 4 + 4 + 1);
339 *new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4 + 4]));
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200340 break;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200341#if ENABLE_FEATURE_UDHCPC6_RFC3646
342 case D6_OPT_DNS_SERVERS: {
343 char *dlist;
344
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200345 /* Make sure payload-size is a multiple of 16 */
346 if ((option[3] & 0x0f) != 0)
347 break;
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200348
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200349 /* Get the number of addresses on the option */
350 addrs = option[3] >> 4;
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200351
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200352 /* Setup environment variable */
353 *new_env() = dlist = xmalloc(4 + addrs * 40 - 1);
354 dlist = stpcpy(dlist, "dns=");
355 option_offset = 0;
356
357 while (addrs--) {
358 sprint_nip6(dlist, option + 4 + option_offset);
359 dlist += 39;
360 option_offset += 16;
361 if (addrs)
362 *dlist++ = ' ';
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200363 }
364
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200365 break;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200366 }
367 case D6_OPT_DOMAIN_LIST: {
368 char *dlist;
369
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200370 dlist = dname_dec(option + 4, (option[2] << 8) | option[3], "search=");
371 if (!dlist)
372 break;
373 *new_env() = dlist;
374 break;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200375 }
376#endif
377#if ENABLE_FEATURE_UDHCPC6_RFC4704
378 case D6_OPT_CLIENT_FQDN: {
379 char *dlist;
380
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200381 if (option[3] == 0)
382 break;
383 /* Work around broken ISC DHCPD6.
384 * ISC DHCPD6 does not implement RFC 4704 correctly: It says the first
385 * byte of option-payload should contain flags where the bits 7-3 are
386 * reserved for future use and MUST be zero. Instead ISC DHCPD6 just
387 * writes the entire FQDN as string to option-payload. We assume a
388 * broken server here if any of the reserved bits are set.
389 */
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200390 if (option[4] & 0xf8) {
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200391 *new_env() = xasprintf("fqdn=%.*s", (int)option[3], (char*)option + 4);
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200392 break;
393 }
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200394 dlist = dname_dec(option + 5, (/*(option[2] << 8) |*/ option[3]) - 1, "fqdn=");
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200395 if (!dlist)
396 break;
397 *new_env() = dlist;
398 break;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200399 }
400#endif
401#if ENABLE_FEATURE_UDHCPC6_RFC4833
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200402 /* RFC 4833 Timezones */
403 case D6_OPT_TZ_POSIX:
404 *new_env() = xasprintf("tz=%.*s", (int)option[3], (char*)option + 4);
405 break;
406 case D6_OPT_TZ_NAME:
407 *new_env() = xasprintf("tz_name=%.*s", (int)option[3], (char*)option + 4);
408 break;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200409#endif
Samuel Mendoza-Jonas23cbd7d2018-05-14 14:29:12 +1000410 case D6_OPT_BOOT_URL:
411 case D6_OPT_BOOT_PARAM:
Samuel Mendoza-Jonas30f4d522018-05-14 14:29:13 +1000412 case 0xd1: /* DHCP_PXE_CONF_FILE */
413 case 0xd2: /* DHCP_PXE_PATH_PREFIX */
Samuel Mendoza-Jonas23cbd7d2018-05-14 14:29:12 +1000414 {
415 char *tmp = string_option_to_env(option, option_end);
416 if (tmp)
417 *new_env() = tmp;
418 break;
419 }
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100420 }
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100421 len_m4 -= 4 + option[3];
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200422 option += 4 + option[3];
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100423 }
424}
425
David Decotigny8f48fc02018-05-24 08:30:15 -0700426static char **fill_envp(const uint8_t *option, const uint8_t *option_end)
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100427{
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100428 char **envp, **curr;
429
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100430 client6_data.env_ptr = NULL;
431 client6_data.env_idx = 0;
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100432
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100433 *new_env() = xasprintf("interface=%s", client_config.interface);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100434
David Decotigny8f48fc02018-05-24 08:30:15 -0700435 if (option)
436 option_to_env(option, option_end);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100437
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100438 envp = curr = client6_data.env_ptr;
439 while (*curr)
440 putenv(*curr++);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100441
442 return envp;
443}
444
445/* Call a script with a par file and env vars */
David Decotigny8f48fc02018-05-24 08:30:15 -0700446static void d6_run_script(const uint8_t *option, const uint8_t *option_end,
447 const char *name)
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100448{
449 char **envp, **curr;
450 char *argv[3];
451
David Decotigny8f48fc02018-05-24 08:30:15 -0700452 envp = fill_envp(option, option_end);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100453
454 /* call script */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +0200455 log1("executing %s %s", client_config.script, name);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100456 argv[0] = (char*) client_config.script;
457 argv[1] = (char*) name;
458 argv[2] = NULL;
459 spawn_and_wait(argv);
460
461 for (curr = envp; *curr; curr++) {
462 log2(" %s", *curr);
463 bb_unsetenv_and_free(*curr);
464 }
465 free(envp);
466}
467
David Decotigny8f48fc02018-05-24 08:30:15 -0700468/* Call a script with a par file and no env var */
469static void d6_run_script_no_option(const char *name)
470{
471 d6_run_script(NULL, NULL, name);
472}
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100473
474/*** Sending/receiving packets ***/
475
476static ALWAYS_INLINE uint32_t random_xid(void)
477{
478 uint32_t t = rand() & htonl(0x00ffffff);
479 return t;
480}
481
482/* Initialize the packet with the proper defaults */
483static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid)
484{
485 struct d6_option *clientid;
486
487 memset(packet, 0, sizeof(*packet));
488
489 packet->d6_xid32 = xid;
490 packet->d6_msg_type = type;
491
492 clientid = (void*)client_config.clientid;
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200493 return mempcpy(packet->d6_options, clientid, clientid->len + 2+2);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100494}
495
496static uint8_t *add_d6_client_options(uint8_t *ptr)
497{
Denys Vlasenko60275972018-05-14 11:06:35 +0200498 struct option_set *curr;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200499 uint8_t *start = ptr;
500 unsigned option;
Denys Vlasenko60275972018-05-14 11:06:35 +0200501 uint16_t len;
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100502
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200503 ptr += 4;
504 for (option = 1; option < 256; option++) {
505 if (client_config.opt_mask[option >> 3] & (1 << (option & 7))) {
506 ptr[0] = (option >> 8);
507 ptr[1] = option;
508 ptr += 2;
509 }
510 }
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100511
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200512 if ((ptr - start - 4) != 0) {
513 start[0] = (D6_OPT_ORO >> 8);
514 start[1] = D6_OPT_ORO;
515 start[2] = ((ptr - start - 4) >> 8);
516 start[3] = (ptr - start - 4);
517 } else
518 ptr = start;
519
520#if ENABLE_FEATURE_UDHCPC6_RFC4704
521 ptr = mempcpy(ptr, &opt_fqdn_req, sizeof(opt_fqdn_req));
522#endif
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100523 /* Add -x options if any */
Denys Vlasenko60275972018-05-14 11:06:35 +0200524 curr = client_config.options;
525 while (curr) {
526 len = (curr->data[D6_OPT_LEN] << 8) | curr->data[D6_OPT_LEN + 1];
527 ptr = mempcpy(ptr, curr->data, D6_OPT_DATA + len);
528 curr = curr->next;
529 }
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200530
531 return ptr;
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100532}
533
534static int d6_mcast_from_client_config_ifindex(struct d6_packet *packet, uint8_t *end)
535{
536 static const uint8_t FF02__1_2[16] = {
537 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
538 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
539 };
540
541 return d6_send_raw_packet(
542 packet, (end - (uint8_t*) packet),
Denys Vlasenkoe09f5e32017-03-27 22:10:15 +0200543 /*src*/ &client6_data.ll_ip6, CLIENT_PORT6,
Denys Vlasenko04ac6e02013-01-28 15:25:35 +0100544 /*dst*/ (struct in6_addr*)FF02__1_2, SERVER_PORT6, MAC_BCAST_ADDR,
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100545 client_config.ifindex
546 );
547}
548
549/* Milticast a DHCPv6 Solicit packet to the network, with an optionally requested IP.
550 *
551 * RFC 3315 17.1.1. Creation of Solicit Messages
552 *
553 * The client MUST include a Client Identifier option to identify itself
554 * to the server. The client includes IA options for any IAs to which
555 * it wants the server to assign addresses. The client MAY include
556 * addresses in the IAs as a hint to the server about addresses for
557 * which the client has a preference. ...
558 *
559 * The client uses IA_NA options to request the assignment of non-
560 * temporary addresses and uses IA_TA options to request the assignment
561 * of temporary addresses. Either IA_NA or IA_TA options, or a
562 * combination of both, can be included in DHCP messages.
563 *
564 * The client SHOULD include an Option Request option (see section 22.7)
565 * to indicate the options the client is interested in receiving. The
566 * client MAY additionally include instances of those options that are
567 * identified in the Option Request option, with data values as hints to
568 * the server about parameter values the client would like to have
569 * returned.
570 *
571 * The client includes a Reconfigure Accept option (see section 22.20)
572 * if the client is willing to accept Reconfigure messages from the
573 * server.
574 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
575 | OPTION_CLIENTID | option-len |
576 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
577 . .
578 . DUID .
579 . (variable length) .
580 . .
581 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
582
583
584 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
585 | OPTION_IA_NA | option-len |
586 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
587 | IAID (4 octets) |
588 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
589 | T1 |
590 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
591 | T2 |
592 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
593 | |
594 . IA_NA-options .
595 . .
596 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
597
598
599 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
600 | OPTION_IAADDR | option-len |
601 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
602 | |
603 | IPv6 address |
604 | |
605 | |
606 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
607 | preferred-lifetime |
608 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
609 | valid-lifetime |
610 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
611 . .
612 . IAaddr-options .
613 . .
614 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
615
616
617 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
618 | OPTION_ORO | option-len |
619 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
620 | requested-option-code-1 | requested-option-code-2 |
621 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
622 | ... |
623 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
624
625
626 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
627 | OPTION_RECONF_ACCEPT | 0 |
628 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
629 */
630/* NOINLINE: limit stack usage in caller */
631static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ipv6)
632{
633 struct d6_packet packet;
634 uint8_t *opt_ptr;
635 unsigned len;
636
637 /* Fill in: msg type, client id */
638 opt_ptr = init_d6_packet(&packet, D6_MSG_SOLICIT, xid);
639
640 /* Create new IA_NA, optionally with included IAADDR with requested IP */
641 free(client6_data.ia_na);
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100642 client6_data.ia_na = NULL;
643 if (option_mask32 & OPT_r) {
644 len = requested_ipv6 ? 2+2+4+4+4 + 2+2+16+4+4 : 2+2+4+4+4;
645 client6_data.ia_na = xzalloc(len);
646 client6_data.ia_na->code = D6_OPT_IA_NA;
647 client6_data.ia_na->len = len - 4;
648 *(uint32_t*)client6_data.ia_na->data = rand(); /* IAID */
649 if (requested_ipv6) {
650 struct d6_option *iaaddr = (void*)(client6_data.ia_na->data + 4+4+4);
651 iaaddr->code = D6_OPT_IAADDR;
652 iaaddr->len = 16+4+4;
653 memcpy(iaaddr->data, requested_ipv6, 16);
654 }
655 opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, len);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100656 }
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100657
658 /* IA_PD */
659 free(client6_data.ia_pd);
660 client6_data.ia_pd = NULL;
661 if (option_mask32 & OPT_d) {
662 len = 2+2+4+4+4;
663 client6_data.ia_pd = xzalloc(len);
664 client6_data.ia_pd->code = D6_OPT_IA_PD;
665 client6_data.ia_pd->len = len - 4;
666 *(uint32_t*)client6_data.ia_pd->data = rand(); /* IAID */
667 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, len);
668 }
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100669
670 /* Add options:
671 * "param req" option according to -O, options specified with -x
672 */
673 opt_ptr = add_d6_client_options(opt_ptr);
674
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +0200675 bb_error_msg("sending %s", "discover");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100676 return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
677}
678
679/* Multicast a DHCPv6 request message
680 *
681 * RFC 3315 18.1.1. Creation and Transmission of Request Messages
682 *
683 * The client uses a Request message to populate IAs with addresses and
684 * obtain other configuration information. The client includes one or
685 * more IA options in the Request message. The server then returns
686 * addresses and other information about the IAs to the client in IA
687 * options in a Reply message.
688 *
689 * The client generates a transaction ID and inserts this value in the
690 * "transaction-id" field.
691 *
692 * The client places the identifier of the destination server in a
693 * Server Identifier option.
694 *
695 * The client MUST include a Client Identifier option to identify itself
696 * to the server. The client adds any other appropriate options,
697 * including one or more IA options (if the client is requesting that
698 * the server assign it some network addresses).
699 *
700 * The client MUST include an Option Request option (see section 22.7)
701 * to indicate the options the client is interested in receiving. The
702 * client MAY include options with data values as hints to the server
703 * about parameter values the client would like to have returned.
704 *
705 * The client includes a Reconfigure Accept option (see section 22.20)
706 * indicating whether or not the client is willing to accept Reconfigure
707 * messages from the server.
708 */
709/* NOINLINE: limit stack usage in caller */
710static NOINLINE int send_d6_select(uint32_t xid)
711{
712 struct d6_packet packet;
713 uint8_t *opt_ptr;
714
715 /* Fill in: msg type, client id */
716 opt_ptr = init_d6_packet(&packet, D6_MSG_REQUEST, xid);
717
718 /* server id */
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200719 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100720 /* IA NA (contains requested IP) */
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100721 if (client6_data.ia_na)
722 opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
723 /* IA PD */
724 if (client6_data.ia_pd)
725 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100726
727 /* Add options:
728 * "param req" option according to -O, options specified with -x
729 */
730 opt_ptr = add_d6_client_options(opt_ptr);
731
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +0200732 bb_error_msg("sending %s", "select");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100733 return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
734}
735
736/* Unicast or broadcast a DHCP renew message
737 *
738 * RFC 3315 18.1.3. Creation and Transmission of Renew Messages
739 *
740 * To extend the valid and preferred lifetimes for the addresses
741 * associated with an IA, the client sends a Renew message to the server
742 * from which the client obtained the addresses in the IA containing an
743 * IA option for the IA. The client includes IA Address options in the
744 * IA option for the addresses associated with the IA. The server
745 * determines new lifetimes for the addresses in the IA according to the
746 * administrative configuration of the server. The server may also add
747 * new addresses to the IA. The server may remove addresses from the IA
748 * by setting the preferred and valid lifetimes of those addresses to
749 * zero.
750 *
751 * The server controls the time at which the client contacts the server
752 * to extend the lifetimes on assigned addresses through the T1 and T2
753 * parameters assigned to an IA.
754 *
755 * At time T1 for an IA, the client initiates a Renew/Reply message
756 * exchange to extend the lifetimes on any addresses in the IA. The
757 * client includes an IA option with all addresses currently assigned to
758 * the IA in its Renew message.
759 *
760 * If T1 or T2 is set to 0 by the server (for an IA_NA) or there are no
761 * T1 or T2 times (for an IA_TA), the client may send a Renew or Rebind
762 * message, respectively, at the client's discretion.
763 *
764 * The client sets the "msg-type" field to RENEW. The client generates
765 * a transaction ID and inserts this value in the "transaction-id"
766 * field.
767 *
768 * The client places the identifier of the destination server in a
769 * Server Identifier option.
770 *
771 * The client MUST include a Client Identifier option to identify itself
772 * to the server. The client adds any appropriate options, including
773 * one or more IA options. The client MUST include the list of
774 * addresses the client currently has associated with the IAs in the
775 * Renew message.
776 *
777 * The client MUST include an Option Request option (see section 22.7)
778 * to indicate the options the client is interested in receiving. The
779 * client MAY include options with data values as hints to the server
780 * about parameter values the client would like to have returned.
781 */
782/* NOINLINE: limit stack usage in caller */
783static NOINLINE int send_d6_renew(uint32_t xid, struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
784{
785 struct d6_packet packet;
786 uint8_t *opt_ptr;
787
788 /* Fill in: msg type, client id */
789 opt_ptr = init_d6_packet(&packet, DHCPREQUEST, xid);
790
791 /* server id */
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200792 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100793 /* IA NA (contains requested IP) */
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100794 if (client6_data.ia_na)
795 opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
796 /* IA PD */
797 if (client6_data.ia_pd)
798 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100799
800 /* Add options:
801 * "param req" option according to -O, options specified with -x
802 */
803 opt_ptr = add_d6_client_options(opt_ptr);
804
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +0200805 bb_error_msg("sending %s", "renew");
Denys Vlasenko148788e2018-06-21 17:36:22 +0200806 if (server_ipv6)
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100807 return d6_send_kernel_packet(
808 &packet, (opt_ptr - (uint8_t*) &packet),
Denys Vlasenko04ac6e02013-01-28 15:25:35 +0100809 our_cur_ipv6, CLIENT_PORT6,
Denys Vlasenkoed898ed2017-03-27 22:32:44 +0200810 server_ipv6, SERVER_PORT6,
811 client_config.ifindex
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100812 );
813 return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
814}
815
816/* Unicast a DHCP release message */
817static int send_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
818{
819 struct d6_packet packet;
820 uint8_t *opt_ptr;
821
822 /* Fill in: msg type, client id */
823 opt_ptr = init_d6_packet(&packet, D6_MSG_RELEASE, random_xid());
824 /* server id */
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200825 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100826 /* IA NA (contains our current IP) */
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100827 if (client6_data.ia_na)
828 opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
829 /* IA PD */
830 if (client6_data.ia_pd)
831 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100832
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +0200833 bb_error_msg("sending %s", "release");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100834 return d6_send_kernel_packet(
835 &packet, (opt_ptr - (uint8_t*) &packet),
Denys Vlasenko04ac6e02013-01-28 15:25:35 +0100836 our_cur_ipv6, CLIENT_PORT6,
Denys Vlasenkoed898ed2017-03-27 22:32:44 +0200837 server_ipv6, SERVER_PORT6,
838 client_config.ifindex
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100839 );
840}
841
842/* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */
843/* NOINLINE: limit stack usage in caller */
Denys Vlasenkoed898ed2017-03-27 22:32:44 +0200844static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6, struct d6_packet *d6_pkt, int fd)
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100845{
846 int bytes;
847 struct ip6_udp_d6_packet packet;
848
849 bytes = safe_read(fd, &packet, sizeof(packet));
850 if (bytes < 0) {
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +0200851 log1("packet read error, ignoring");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100852 /* NB: possible down interface, etc. Caller should pause. */
853 return bytes; /* returns -1 */
854 }
855
856 if (bytes < (int) (sizeof(packet.ip6) + sizeof(packet.udp))) {
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +0200857 log1("packet is too short, ignoring");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100858 return -2;
859 }
860
861 if (bytes < sizeof(packet.ip6) + ntohs(packet.ip6.ip6_plen)) {
862 /* packet is bigger than sizeof(packet), we did partial read */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +0200863 log1("oversized packet, ignoring");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100864 return -2;
865 }
866
867 /* ignore any extra garbage bytes */
868 bytes = sizeof(packet.ip6) + ntohs(packet.ip6.ip6_plen);
869
870 /* make sure its the right packet for us, and that it passes sanity checks */
871 if (packet.ip6.ip6_nxt != IPPROTO_UDP
872 || (packet.ip6.ip6_vfc >> 4) != 6
Denys Vlasenko04ac6e02013-01-28 15:25:35 +0100873 || packet.udp.dest != htons(CLIENT_PORT6)
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100874 /* || bytes > (int) sizeof(packet) - can't happen */
875 || packet.udp.len != packet.ip6.ip6_plen
876 ) {
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +0200877 log1("unrelated/bogus packet, ignoring");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100878 return -2;
879 }
880
881//How to do this for ipv6?
882// /* verify UDP checksum. IP header has to be modified for this */
883// memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
884// /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
885// packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
886// check = packet.udp.check;
887// packet.udp.check = 0;
888// if (check && check != inet_cksum((uint16_t *)&packet, bytes)) {
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +0200889// log1("packet with bad UDP checksum received, ignoring");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100890// return -2;
891// }
892
Denys Vlasenkoed898ed2017-03-27 22:32:44 +0200893 if (peer_ipv6)
894 *peer_ipv6 = packet.ip6.ip6_src; /* struct copy */
895
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +0200896 log1("received %s", "a packet");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100897 d6_dump_packet(&packet.data);
898
899 bytes -= sizeof(packet.ip6) + sizeof(packet.udp);
900 memcpy(d6_pkt, &packet.data, bytes);
901 return bytes;
902}
903
904
905/*** Main ***/
906
907static int sockfd = -1;
908
909#define LISTEN_NONE 0
910#define LISTEN_KERNEL 1
911#define LISTEN_RAW 2
912static smallint listen_mode;
913
914/* initial state: (re)start DHCP negotiation */
915#define INIT_SELECTING 0
916/* discover was sent, DHCPOFFER reply received */
917#define REQUESTING 1
918/* select/renew was sent, DHCPACK reply received */
919#define BOUND 2
920/* half of lease passed, want to renew it by sending unicast renew requests */
921#define RENEWING 3
922/* renew requests were not answered, lease is almost over, send broadcast renew */
923#define REBINDING 4
924/* manually requested renew (SIGUSR1) */
925#define RENEW_REQUESTED 5
926/* release, possibly manually requested (SIGUSR2) */
927#define RELEASED 6
928static smallint state;
929
930static int d6_raw_socket(int ifindex)
931{
932 int fd;
933 struct sockaddr_ll sock;
934
935 /*
936 * Comment:
937 *
938 * I've selected not to see LL header, so BPF doesn't see it, too.
939 * The filter may also pass non-IP and non-ARP packets, but we do
940 * a more complete check when receiving the message in userspace.
941 *
942 * and filter shamelessly stolen from:
943 *
944 * http://www.flamewarmaster.de/software/dhcpclient/
945 *
946 * There are a few other interesting ideas on that page (look under
947 * "Motivation"). Use of netlink events is most interesting. Think
948 * of various network servers listening for events and reconfiguring.
949 * That would obsolete sending HUP signals and/or make use of restarts.
950 *
951 * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
952 * License: GPL v2.
953 *
954 * TODO: make conditional?
955 */
956#if 0
957 static const struct sock_filter filter_instr[] = {
958 /* load 9th byte (protocol) */
959 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
960 /* jump to L1 if it is IPPROTO_UDP, else to L4 */
961 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6),
962 /* L1: load halfword from offset 6 (flags and frag offset) */
963 BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6),
964 /* jump to L4 if any bits in frag offset field are set, else to L2 */
965 BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0),
966 /* L2: skip IP header (load index reg with header len) */
967 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),
968 /* load udp destination port from halfword[header_len + 2] */
969 BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2),
Denys Vlasenko04ac6e02013-01-28 15:25:35 +0100970 /* jump to L3 if udp dport is CLIENT_PORT6, else to L4 */
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100971 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1),
972 /* L3: accept packet */
Denys Vlasenkoffc3a932014-02-19 14:17:11 +0100973 BPF_STMT(BPF_RET|BPF_K, 0x7fffffff),
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100974 /* L4: discard packet */
975 BPF_STMT(BPF_RET|BPF_K, 0),
976 };
977 static const struct sock_fprog filter_prog = {
978 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
979 /* casting const away: */
980 .filter = (struct sock_filter *) filter_instr,
981 };
982#endif
983
Denys Vlasenko168f0ef2017-07-21 12:04:22 +0200984 log2("opening raw socket on ifindex %d", ifindex);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100985
986 fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6));
Denys Vlasenko168f0ef2017-07-21 12:04:22 +0200987 log2("got raw socket fd %d", fd);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100988
Denys Vlasenko2b9acc62017-09-29 14:09:02 +0200989 memset(&sock, 0, sizeof(sock)); /* let's be deterministic */
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100990 sock.sll_family = AF_PACKET;
991 sock.sll_protocol = htons(ETH_P_IPV6);
992 sock.sll_ifindex = ifindex;
Denys Vlasenko2b9acc62017-09-29 14:09:02 +0200993 /*sock.sll_hatype = ARPHRD_???;*/
994 /*sock.sll_pkttype = PACKET_???;*/
995 /*sock.sll_halen = ???;*/
996 /*sock.sll_addr[8] = ???;*/
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100997 xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
998
999#if 0
Denys Vlasenko04ac6e02013-01-28 15:25:35 +01001000 if (CLIENT_PORT6 == 546) {
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001001 /* Use only if standard port is in use */
1002 /* Ignoring error (kernel may lack support for this) */
1003 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
1004 sizeof(filter_prog)) >= 0)
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001005 log1("attached filter to raw socket fd %d", fd); // log?
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001006 }
1007#endif
1008
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001009 log1("created raw socket");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001010
1011 return fd;
1012}
1013
1014static void change_listen_mode(int new_mode)
1015{
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001016 log1("entering listen mode: %s",
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001017 new_mode != LISTEN_NONE
1018 ? (new_mode == LISTEN_KERNEL ? "kernel" : "raw")
1019 : "none"
1020 );
1021
1022 listen_mode = new_mode;
1023 if (sockfd >= 0) {
1024 close(sockfd);
1025 sockfd = -1;
1026 }
1027 if (new_mode == LISTEN_KERNEL)
Denys Vlasenko04ac6e02013-01-28 15:25:35 +01001028 sockfd = udhcp_listen_socket(/*INADDR_ANY,*/ CLIENT_PORT6, client_config.interface);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001029 else if (new_mode != LISTEN_NONE)
1030 sockfd = d6_raw_socket(client_config.ifindex);
1031 /* else LISTEN_NONE: sockfd stays closed */
1032}
1033
1034/* Called only on SIGUSR1 */
1035static void perform_renew(void)
1036{
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001037 bb_error_msg("performing DHCP renew");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001038 switch (state) {
1039 case BOUND:
1040 change_listen_mode(LISTEN_KERNEL);
1041 case RENEWING:
1042 case REBINDING:
1043 state = RENEW_REQUESTED;
1044 break;
1045 case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
David Decotigny8f48fc02018-05-24 08:30:15 -07001046 d6_run_script_no_option("deconfig");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001047 case REQUESTING:
1048 case RELEASED:
1049 change_listen_mode(LISTEN_RAW);
1050 state = INIT_SELECTING;
1051 break;
1052 case INIT_SELECTING:
1053 break;
1054 }
1055}
1056
1057static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
1058{
1059 /* send release packet */
Denys Vlasenko44399e02016-07-03 20:26:44 +02001060 if (state == BOUND
1061 || state == RENEWING
1062 || state == REBINDING
1063 || state == RENEW_REQUESTED
1064 ) {
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001065 bb_error_msg("unicasting a release");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001066 send_d6_release(server_ipv6, our_cur_ipv6); /* unicast */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001067 }
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001068 bb_error_msg("entering released state");
Peter Korsgaardb6355e22016-08-26 18:46:34 +02001069/*
1070 * We can be here on: SIGUSR2,
1071 * or on exit (SIGTERM) and -R "release on quit" is specified.
1072 * Users requested to be notified in all cases, even if not in one
1073 * of the states above.
1074 */
David Decotigny8f48fc02018-05-24 08:30:15 -07001075 d6_run_script_no_option("deconfig");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001076 change_listen_mode(LISTEN_NONE);
1077 state = RELEASED;
1078}
1079
1080///static uint8_t* alloc_dhcp_option(int code, const char *str, int extra)
1081///{
1082/// uint8_t *storage;
1083/// int len = strnlen(str, 255);
1084/// storage = xzalloc(len + extra + OPT_DATA);
1085/// storage[OPT_CODE] = code;
1086/// storage[OPT_LEN] = len + extra;
1087/// memcpy(storage + extra + OPT_DATA, str, len);
1088/// return storage;
1089///}
1090
1091#if BB_MMU
1092static void client_background(void)
1093{
1094 bb_daemonize(0);
1095 logmode &= ~LOGMODE_STDIO;
1096 /* rewrite pidfile, as our pid is different now */
1097 write_pidfile(client_config.pidfile);
1098}
1099#endif
1100
1101//usage:#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
1102//usage:# define IF_UDHCP_VERBOSE(...) __VA_ARGS__
1103//usage:#else
1104//usage:# define IF_UDHCP_VERBOSE(...)
1105//usage:#endif
1106//usage:#define udhcpc6_trivial_usage
Denys Vlasenko64211ce2018-01-16 22:23:38 +01001107//usage: "[-fbnq"IF_UDHCP_VERBOSE("v")"odR] [-i IFACE] [-r IPv6] [-s PROG] [-p PIDFILE]\n"
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001108//usage: " [-x OPT:VAL]... [-O OPT]..." IF_FEATURE_UDHCP_PORT(" [-P N]")
1109//usage:#define udhcpc6_full_usage "\n"
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001110//usage: "\n -i IFACE Interface to use (default eth0)"
1111//usage: "\n -p FILE Create pidfile"
1112//usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")"
1113//usage: "\n -B Request broadcast replies"
1114//usage: "\n -t N Send up to N discover packets"
1115//usage: "\n -T N Pause between packets (default 3 seconds)"
1116//usage: "\n -A N Wait N seconds (default 20) after failure"
1117//usage: "\n -f Run in foreground"
1118//usage: USE_FOR_MMU(
1119//usage: "\n -b Background if lease is not obtained"
1120//usage: )
1121//usage: "\n -n Exit if lease is not obtained"
1122//usage: "\n -q Exit after obtaining lease"
1123//usage: "\n -R Release IP on exit"
1124//usage: "\n -S Log to syslog too"
1125//usage: IF_FEATURE_UDHCP_PORT(
Denys Vlasenko7e21f042011-11-08 11:39:41 +01001126//usage: "\n -P N Use port N (default 546)"
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001127//usage: )
1128////usage: IF_FEATURE_UDHCPC_ARPING(
1129////usage: "\n -a Use arping to validate offered address"
1130////usage: )
1131//usage: "\n -O OPT Request option OPT from server (cumulative)"
1132//usage: "\n -o Don't request any options (unless -O is given)"
Denys Vlasenko64211ce2018-01-16 22:23:38 +01001133//usage: "\n -r IPv6 Request this address ('no' to not request any IP)"
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001134//usage: "\n -d Request prefix"
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001135//usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)"
1136//usage: "\n Examples of string, numeric, and hex byte opts:"
1137//usage: "\n -x hostname:bbox - option 12"
1138//usage: "\n -x lease:3600 - option 51 (lease time)"
1139//usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)"
Denys Vlasenko266f6f12018-04-13 13:18:34 +02001140//usage: "\n -x 14:'\"dumpfile\"' - option 14 (shell-quoted)"
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001141//usage: IF_UDHCP_VERBOSE(
1142//usage: "\n -v Verbose"
1143//usage: )
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001144//usage: "\nSignals:"
1145//usage: "\n USR1 Renew lease"
1146//usage: "\n USR2 Release lease"
1147
1148
1149int udhcpc6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1150int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1151{
1152 const char *str_r;
1153 IF_FEATURE_UDHCP_PORT(char *str_P;)
1154 void *clientid_mac_ptr;
1155 llist_t *list_O = NULL;
1156 llist_t *list_x = NULL;
1157 int tryagain_timeout = 20;
1158 int discover_timeout = 3;
1159 int discover_retries = 3;
1160 struct in6_addr srv6_buf;
1161 struct in6_addr ipv6_buf;
1162 struct in6_addr *requested_ipv6;
1163 uint32_t xid = 0;
1164 int packet_num;
1165 int timeout; /* must be signed */
1166 unsigned already_waited_sec;
1167 unsigned opt;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001168 int retval;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001169
Denys Vlasenkodf70a432016-04-21 18:54:36 +02001170 setup_common_bufsiz();
Denys Vlasenkof6dd9e02018-01-19 18:44:19 +01001171 /* We want random_xid to be random */
1172 srand(monotonic_us());
Denys Vlasenkodf70a432016-04-21 18:54:36 +02001173
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001174 /* Default options */
Denys Vlasenko04ac6e02013-01-28 15:25:35 +01001175 IF_FEATURE_UDHCP_PORT(SERVER_PORT6 = 547;)
1176 IF_FEATURE_UDHCP_PORT(CLIENT_PORT6 = 546;)
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001177 client_config.interface = "eth0";
1178 client_config.script = CONFIG_UDHCPC_DEFAULT_SCRIPT;
1179
1180 /* Parse command line */
Denys Vlasenko22542ec2017-08-08 21:55:02 +02001181 opt = getopt32long(argv, "^"
1182 /* O,x: list; -T,-t,-A take numeric param */
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001183 "i:np:qRr:s:T:+t:+SA:+O:*ox:*fd"
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001184 USE_FOR_MMU("b")
1185 ///IF_FEATURE_UDHCPC_ARPING("a")
1186 IF_FEATURE_UDHCP_PORT("P:")
1187 "v"
Denys Vlasenko22542ec2017-08-08 21:55:02 +02001188 "\0" IF_UDHCP_VERBOSE("vv") /* -v is a counter */
Denys Vlasenko036585a2017-08-08 16:38:18 +02001189 , udhcpc6_longopts
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001190 , &client_config.interface, &client_config.pidfile, &str_r /* i,p */
1191 , &client_config.script /* s */
1192 , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */
1193 , &list_O
1194 , &list_x
1195 IF_FEATURE_UDHCP_PORT(, &str_P)
1196 IF_UDHCP_VERBOSE(, &dhcp_verbose)
Denys Vlasenko7e21f042011-11-08 11:39:41 +01001197 );
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001198 requested_ipv6 = NULL;
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001199 option_mask32 |= OPT_r;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001200 if (opt & OPT_r) {
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001201 if (strcmp(str_r, "no") == 0) {
1202 option_mask32 -= OPT_r;
1203 } else {
1204 if (inet_pton(AF_INET6, str_r, &ipv6_buf) <= 0)
1205 bb_error_msg_and_die("bad IPv6 address '%s'", str_r);
1206 requested_ipv6 = &ipv6_buf;
1207 }
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001208 }
1209#if ENABLE_FEATURE_UDHCP_PORT
1210 if (opt & OPT_P) {
Denys Vlasenko04ac6e02013-01-28 15:25:35 +01001211 CLIENT_PORT6 = xatou16(str_P);
1212 SERVER_PORT6 = CLIENT_PORT6 + 1;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001213 }
1214#endif
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001215 while (list_O) {
1216 char *optstr = llist_pop(&list_O);
1217 unsigned n = bb_strtou(optstr, NULL, 0);
1218 if (errno || n > 254) {
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +02001219 n = udhcp_option_idx(optstr, d6_option_strings);
1220 n = d6_optflags[n].code;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001221 }
1222 client_config.opt_mask[n >> 3] |= 1 << (n & 7);
1223 }
Denys Vlasenko293c9452012-07-27 13:25:07 +02001224 if (!(opt & OPT_o)) {
Denys Vlasenko293c9452012-07-27 13:25:07 +02001225 unsigned i, n;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +02001226 for (i = 0; (n = d6_optflags[i].code) != 0; i++) {
1227 if (d6_optflags[i].flags & OPTION_REQ) {
Denys Vlasenko293c9452012-07-27 13:25:07 +02001228 client_config.opt_mask[n >> 3] |= 1 << (n & 7);
1229 }
1230 }
Denys Vlasenko293c9452012-07-27 13:25:07 +02001231 }
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001232 while (list_x) {
Denys Vlasenko266f6f12018-04-13 13:18:34 +02001233 char *optstr = xstrdup(llist_pop(&list_x));
Denys Vlasenko60275972018-05-14 11:06:35 +02001234 udhcp_str2optset(optstr, &client_config.options,
1235 d6_optflags, d6_option_strings,
1236 /*dhcpv6:*/ 1
1237 );
Denys Vlasenko266f6f12018-04-13 13:18:34 +02001238 free(optstr);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001239 }
1240
Denys Vlasenkoe09f5e32017-03-27 22:10:15 +02001241 if (d6_read_interface(client_config.interface,
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001242 &client_config.ifindex,
Denys Vlasenkoe09f5e32017-03-27 22:10:15 +02001243 &client6_data.ll_ip6,
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001244 client_config.client_mac)
1245 ) {
1246 return 1;
1247 }
1248
1249 /* Create client ID based on mac, set clientid_mac_ptr */
1250 {
1251 struct d6_option *clientid;
1252 clientid = xzalloc(2+2+2+2+6);
1253 clientid->code = D6_OPT_CLIENTID;
1254 clientid->len = 2+2+6;
Denys Vlasenko68c5b282011-11-07 16:21:24 +01001255 clientid->data[1] = 3; /* DUID-LL */
1256 clientid->data[3] = 1; /* ethernet */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001257 clientid_mac_ptr = clientid->data + 2+2;
1258 memcpy(clientid_mac_ptr, client_config.client_mac, 6);
1259 client_config.clientid = (void*)clientid;
1260 }
1261
1262#if !BB_MMU
1263 /* on NOMMU reexec (i.e., background) early */
1264 if (!(opt & OPT_f)) {
1265 bb_daemonize_or_rexec(0 /* flags */, argv);
1266 logmode = LOGMODE_NONE;
1267 }
1268#endif
1269 if (opt & OPT_S) {
1270 openlog(applet_name, LOG_PID, LOG_DAEMON);
1271 logmode |= LOGMODE_SYSLOG;
1272 }
1273
1274 /* Make sure fd 0,1,2 are open */
1275 bb_sanitize_stdio();
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001276 /* Create pidfile */
1277 write_pidfile(client_config.pidfile);
1278 /* Goes to stdout (unless NOMMU) and possibly syslog */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001279 bb_error_msg("started, v"BB_VER);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001280 /* Set up the signal pipe */
1281 udhcp_sp_setup();
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001282
1283 state = INIT_SELECTING;
David Decotigny8f48fc02018-05-24 08:30:15 -07001284 d6_run_script_no_option("deconfig");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001285 change_listen_mode(LISTEN_RAW);
1286 packet_num = 0;
1287 timeout = 0;
1288 already_waited_sec = 0;
1289
1290 /* Main event loop. select() waits on signal pipe and possibly
1291 * on sockfd.
1292 * "continue" statements in code below jump to the top of the loop.
1293 */
1294 for (;;) {
Denys Vlasenko52a515d2017-02-16 23:25:44 +01001295 int tv;
1296 struct pollfd pfds[2];
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001297 struct d6_packet packet;
1298 uint8_t *packet_end;
1299 /* silence "uninitialized!" warning */
1300 unsigned timestamp_before_wait = timestamp_before_wait;
1301
1302 //bb_error_msg("sockfd:%d, listen_mode:%d", sockfd, listen_mode);
1303
1304 /* Was opening raw or udp socket here
1305 * if (listen_mode != LISTEN_NONE && sockfd < 0),
1306 * but on fast network renew responses return faster
1307 * than we open sockets. Thus this code is moved
1308 * to change_listen_mode(). Thus we open listen socket
1309 * BEFORE we send renew request (see "case BOUND:"). */
1310
Denys Vlasenko52a515d2017-02-16 23:25:44 +01001311 udhcp_sp_fd_set(pfds, sockfd);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001312
Denys Vlasenko52a515d2017-02-16 23:25:44 +01001313 tv = timeout - already_waited_sec;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001314 retval = 0;
1315 /* If we already timed out, fall through with retval = 0, else... */
Denys Vlasenko52a515d2017-02-16 23:25:44 +01001316 if (tv > 0) {
Denys Vlasenkode6cb412017-07-24 12:01:28 +02001317 log1("waiting %u seconds", tv);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001318 timestamp_before_wait = (unsigned)monotonic_sec();
Denys Vlasenko7c67f1e2017-02-17 19:20:32 +01001319 retval = poll(pfds, 2, tv < INT_MAX/1000 ? tv * 1000 : INT_MAX);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001320 if (retval < 0) {
1321 /* EINTR? A signal was caught, don't panic */
1322 if (errno == EINTR) {
1323 already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
1324 continue;
1325 }
1326 /* Else: an error occured, panic! */
Denys Vlasenkode6cb412017-07-24 12:01:28 +02001327 bb_perror_msg_and_die("poll");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001328 }
1329 }
1330
1331 /* If timeout dropped to zero, time to become active:
1332 * resend discover/renew/whatever
1333 */
1334 if (retval == 0) {
1335 /* When running on a bridge, the ifindex may have changed
1336 * (e.g. if member interfaces were added/removed
1337 * or if the status of the bridge changed).
1338 * Refresh ifindex and client_mac:
1339 */
Denys Vlasenkoe09f5e32017-03-27 22:10:15 +02001340 if (d6_read_interface(client_config.interface,
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001341 &client_config.ifindex,
Denys Vlasenkoe09f5e32017-03-27 22:10:15 +02001342 &client6_data.ll_ip6,
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001343 client_config.client_mac)
1344 ) {
1345 goto ret0; /* iface is gone? */
1346 }
Denys Vlasenkoe09f5e32017-03-27 22:10:15 +02001347
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001348 memcpy(clientid_mac_ptr, client_config.client_mac, 6);
1349
1350 /* We will restart the wait in any case */
1351 already_waited_sec = 0;
1352
1353 switch (state) {
1354 case INIT_SELECTING:
Felix Fietkau1c7a58d2012-09-27 16:22:24 +02001355 if (!discover_retries || packet_num < discover_retries) {
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001356 if (packet_num == 0)
1357 xid = random_xid();
1358 /* multicast */
1359 send_d6_discover(xid, requested_ipv6);
1360 timeout = discover_timeout;
1361 packet_num++;
1362 continue;
1363 }
1364 leasefail:
David Decotigny8f48fc02018-05-24 08:30:15 -07001365 d6_run_script_no_option("leasefail");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001366#if BB_MMU /* -b is not supported on NOMMU */
1367 if (opt & OPT_b) { /* background if no lease */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001368 bb_error_msg("no lease, forking to background");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001369 client_background();
1370 /* do not background again! */
1371 opt = ((opt & ~OPT_b) | OPT_f);
1372 } else
1373#endif
1374 if (opt & OPT_n) { /* abort if no lease */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001375 bb_error_msg("no lease, failing");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001376 retval = 1;
1377 goto ret;
1378 }
1379 /* wait before trying again */
1380 timeout = tryagain_timeout;
1381 packet_num = 0;
1382 continue;
1383 case REQUESTING:
Felix Fietkau1c7a58d2012-09-27 16:22:24 +02001384 if (!discover_retries || packet_num < discover_retries) {
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001385 /* send multicast select packet */
1386 send_d6_select(xid);
1387 timeout = discover_timeout;
1388 packet_num++;
1389 continue;
1390 }
1391 /* Timed out, go back to init state.
1392 * "discover...select...discover..." loops
1393 * were seen in the wild. Treat them similarly
1394 * to "no response to discover" case */
1395 change_listen_mode(LISTEN_RAW);
1396 state = INIT_SELECTING;
1397 goto leasefail;
1398 case BOUND:
1399 /* 1/2 lease passed, enter renewing state */
1400 state = RENEWING;
1401 client_config.first_secs = 0; /* make secs field count from 0 */
1402 change_listen_mode(LISTEN_KERNEL);
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001403 log1("entering renew state");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001404 /* fall right through */
1405 case RENEW_REQUESTED: /* manual (SIGUSR1) renew */
1406 case_RENEW_REQUESTED:
1407 case RENEWING:
1408 if (timeout > 60) {
1409 /* send an unicast renew request */
1410 /* Sometimes observed to fail (EADDRNOTAVAIL) to bind
1411 * a new UDP socket for sending inside send_renew.
1412 * I hazard to guess existing listening socket
1413 * is somehow conflicting with it, but why is it
1414 * not deterministic then?! Strange.
1415 * Anyway, it does recover by eventually failing through
1416 * into INIT_SELECTING state.
1417 */
1418 send_d6_renew(xid, &srv6_buf, requested_ipv6);
1419 timeout >>= 1;
1420 continue;
1421 }
1422 /* Timed out, enter rebinding state */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001423 log1("entering rebinding state");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001424 state = REBINDING;
1425 /* fall right through */
1426 case REBINDING:
1427 /* Switch to bcast receive */
1428 change_listen_mode(LISTEN_RAW);
1429 /* Lease is *really* about to run out,
1430 * try to find DHCP server using broadcast */
1431 if (timeout > 0) {
1432 /* send a broadcast renew request */
1433 send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6);
1434 timeout >>= 1;
1435 continue;
1436 }
1437 /* Timed out, enter init state */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001438 bb_error_msg("lease lost, entering init state");
David Decotigny8f48fc02018-05-24 08:30:15 -07001439 d6_run_script_no_option("deconfig");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001440 state = INIT_SELECTING;
1441 client_config.first_secs = 0; /* make secs field count from 0 */
1442 /*timeout = 0; - already is */
1443 packet_num = 0;
1444 continue;
1445 /* case RELEASED: */
1446 }
1447 /* yah, I know, *you* say it would never happen */
1448 timeout = INT_MAX;
1449 continue; /* back to main loop */
Denys Vlasenko3293bc12018-03-10 19:01:48 +01001450 } /* if poll timed out */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001451
Denys Vlasenko3293bc12018-03-10 19:01:48 +01001452 /* poll() didn't timeout, something happened */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001453
1454 /* Is it a signal? */
Denys Vlasenko3293bc12018-03-10 19:01:48 +01001455 switch (udhcp_sp_read()) {
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001456 case SIGUSR1:
1457 client_config.first_secs = 0; /* make secs field count from 0 */
1458 already_waited_sec = 0;
1459 perform_renew();
1460 if (state == RENEW_REQUESTED) {
1461 /* We might be either on the same network
1462 * (in which case renew might work),
1463 * or we might be on a completely different one
1464 * (in which case renew won't ever succeed).
1465 * For the second case, must make sure timeout
1466 * is not too big, or else we can send
1467 * futile renew requests for hours.
1468 * (Ab)use -A TIMEOUT value (usually 20 sec)
1469 * as a cap on the timeout.
1470 */
1471 if (timeout > tryagain_timeout)
1472 timeout = tryagain_timeout;
1473 goto case_RENEW_REQUESTED;
1474 }
1475 /* Start things over */
1476 packet_num = 0;
1477 /* Kill any timeouts, user wants this to hurry along */
1478 timeout = 0;
1479 continue;
1480 case SIGUSR2:
1481 perform_d6_release(&srv6_buf, requested_ipv6);
1482 timeout = INT_MAX;
1483 continue;
1484 case SIGTERM:
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001485 bb_error_msg("received %s", "SIGTERM");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001486 goto ret0;
1487 }
1488
1489 /* Is it a packet? */
Denys Vlasenko3293bc12018-03-10 19:01:48 +01001490 if (!pfds[1].revents)
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001491 continue; /* no */
1492
1493 {
1494 int len;
1495
1496 /* A packet is ready, read it */
1497 if (listen_mode == LISTEN_KERNEL)
1498 len = d6_recv_kernel_packet(&srv6_buf, &packet, sockfd);
1499 else
1500 len = d6_recv_raw_packet(&srv6_buf, &packet, sockfd);
1501 if (len == -1) {
1502 /* Error is severe, reopen socket */
Denys Vlasenko6f97b302017-09-29 18:17:25 +02001503 bb_error_msg("read error: "STRERROR_FMT", reopening socket" STRERROR_ERRNO);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001504 sleep(discover_timeout); /* 3 seconds by default */
1505 change_listen_mode(listen_mode); /* just close and reopen */
1506 }
1507 /* If this packet will turn out to be unrelated/bogus,
1508 * we will go back and wait for next one.
1509 * Be sure timeout is properly decreased. */
1510 already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
1511 if (len < 0)
1512 continue;
1513 packet_end = (uint8_t*)&packet + len;
1514 }
1515
1516 if ((packet.d6_xid32 & htonl(0x00ffffff)) != xid) {
1517 log1("xid %x (our is %x), ignoring packet",
1518 (unsigned)(packet.d6_xid32 & htonl(0x00ffffff)), (unsigned)xid);
1519 continue;
1520 }
1521
1522 switch (state) {
1523 case INIT_SELECTING:
1524 if (packet.d6_msg_type == D6_MSG_ADVERTISE)
1525 goto type_is_ok;
1526 /* DHCPv6 has "Rapid Commit", when instead of Advertise,
1527 * server sends Reply right away.
1528 * Fall through to check for this case.
1529 */
1530 case REQUESTING:
1531 case RENEWING:
1532 case RENEW_REQUESTED:
1533 case REBINDING:
1534 if (packet.d6_msg_type == D6_MSG_REPLY) {
1535 uint32_t lease_seconds;
Denys Vlasenko14f13202018-01-16 21:47:10 +01001536 struct d6_option *option;
Denys Vlasenko7c44b602018-01-17 13:55:51 +01001537 unsigned address_timeout;
1538 unsigned prefix_timeout;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001539 type_is_ok:
Denys Vlasenko14f13202018-01-16 21:47:10 +01001540 address_timeout = 0;
1541 prefix_timeout = 0;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001542 option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
Denys Vlasenko64d58aa2017-03-27 22:22:09 +02001543 if (option && (option->data[0] | option->data[1]) != 0) {
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001544 /* return to init state */
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001545 bb_error_msg("received DHCP NAK (%u)", option->data[4]);
David Decotigny8f48fc02018-05-24 08:30:15 -07001546 d6_run_script(packet.d6_options,
1547 packet_end, "nak");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001548 if (state != REQUESTING)
David Decotigny8f48fc02018-05-24 08:30:15 -07001549 d6_run_script_no_option("deconfig");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001550 change_listen_mode(LISTEN_RAW);
1551 sleep(3); /* avoid excessive network traffic */
1552 state = INIT_SELECTING;
1553 client_config.first_secs = 0; /* make secs field count from 0 */
1554 requested_ipv6 = NULL;
1555 timeout = 0;
1556 packet_num = 0;
1557 already_waited_sec = 0;
1558 continue;
1559 }
1560 option = d6_copy_option(packet.d6_options, packet_end, D6_OPT_SERVERID);
1561 if (!option) {
1562 bb_error_msg("no server ID, ignoring packet");
1563 continue;
1564 /* still selecting - this server looks bad */
1565 }
1566//Note: we do not bother comparing server IDs in Advertise and Reply msgs.
1567//server_id variable is used solely for creation of proper server_id option
1568//in outgoing packets. (why DHCPv6 even introduced it is a mystery).
1569 free(client6_data.server_id);
1570 client6_data.server_id = option;
1571 if (packet.d6_msg_type == D6_MSG_ADVERTISE) {
1572 /* enter requesting state */
1573 state = REQUESTING;
1574 timeout = 0;
1575 packet_num = 0;
1576 already_waited_sec = 0;
1577 continue;
1578 }
1579 /* It's a D6_MSG_REPLY */
1580/*
1581 * RFC 3315 18.1.8. Receipt of Reply Messages
1582 *
1583 * Upon the receipt of a valid Reply message in response to a Solicit
1584 * (with a Rapid Commit option), Request, Confirm, Renew, Rebind or
1585 * Information-request message, the client extracts the configuration
1586 * information contained in the Reply. The client MAY choose to report
1587 * any status code or message from the status code option in the Reply
1588 * message.
1589 *
1590 * The client SHOULD perform duplicate address detection [17] on each of
1591 * the addresses in any IAs it receives in the Reply message before
1592 * using that address for traffic. If any of the addresses are found to
1593 * be in use on the link, the client sends a Decline message to the
1594 * server as described in section 18.1.7.
1595 *
1596 * If the Reply was received in response to a Solicit (with a Rapid
1597 * Commit option), Request, Renew or Rebind message, the client updates
1598 * the information it has recorded about IAs from the IA options
1599 * contained in the Reply message:
1600 *
1601 * - Record T1 and T2 times.
1602 *
1603 * - Add any new addresses in the IA option to the IA as recorded by
1604 * the client.
1605 *
1606 * - Update lifetimes for any addresses in the IA option that the
1607 * client already has recorded in the IA.
1608 *
1609 * - Discard any addresses from the IA, as recorded by the client, that
1610 * have a valid lifetime of 0 in the IA Address option.
1611 *
1612 * - Leave unchanged any information about addresses the client has
1613 * recorded in the IA but that were not included in the IA from the
1614 * server.
1615 *
1616 * Management of the specific configuration information is detailed in
1617 * the definition of each option in section 22.
1618 *
1619 * If the client receives a Reply message with a Status Code containing
1620 * UnspecFail, the server is indicating that it was unable to process
1621 * the message due to an unspecified failure condition. If the client
1622 * retransmits the original message to the same server to retry the
1623 * desired operation, the client MUST limit the rate at which it
1624 * retransmits the message and limit the duration of the time during
1625 * which it retransmits the message.
1626 *
1627 * When the client receives a Reply message with a Status Code option
1628 * with the value UseMulticast, the client records the receipt of the
1629 * message and sends subsequent messages to the server through the
1630 * interface on which the message was received using multicast. The
1631 * client resends the original message using multicast.
1632 *
1633 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1634 * | OPTION_IA_NA | option-len |
1635 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1636 * | IAID (4 octets) |
1637 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1638 * | T1 |
1639 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1640 * | T2 |
1641 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1642 * | |
1643 * . IA_NA-options .
1644 * . .
1645 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1646 *
1647 *
1648 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1649 * | OPTION_IAADDR | option-len |
1650 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1651 * | |
1652 * | IPv6 address |
1653 * | |
1654 * | |
1655 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1656 * | preferred-lifetime |
1657 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1658 * | valid-lifetime |
1659 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1660 * . .
1661 * . IAaddr-options .
1662 * . .
1663 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1664 */
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001665 if (option_mask32 & OPT_r) {
Denys Vlasenko14f13202018-01-16 21:47:10 +01001666 struct d6_option *iaaddr;
1667
Denys Vlasenko1e8d79b2018-01-16 21:35:39 +01001668 free(client6_data.ia_na);
1669 client6_data.ia_na = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_NA);
1670 if (!client6_data.ia_na) {
1671 bb_error_msg("no %s option, ignoring packet", "IA_NA");
1672 continue;
1673 }
1674 if (client6_data.ia_na->len < (4 + 4 + 4) + (2 + 2 + 16 + 4 + 4)) {
Denys Vlasenko14f13202018-01-16 21:47:10 +01001675 bb_error_msg("%s option is too short:%d bytes",
1676 "IA_NA", client6_data.ia_na->len);
Denys Vlasenko1e8d79b2018-01-16 21:35:39 +01001677 continue;
1678 }
1679 iaaddr = d6_find_option(client6_data.ia_na->data + 4 + 4 + 4,
1680 client6_data.ia_na->data + client6_data.ia_na->len,
1681 D6_OPT_IAADDR
1682 );
1683 if (!iaaddr) {
1684 bb_error_msg("no %s option, ignoring packet", "IAADDR");
1685 continue;
1686 }
1687 if (iaaddr->len < (16 + 4 + 4)) {
Denys Vlasenko14f13202018-01-16 21:47:10 +01001688 bb_error_msg("%s option is too short:%d bytes",
1689 "IAADDR", iaaddr->len);
Denys Vlasenko1e8d79b2018-01-16 21:35:39 +01001690 continue;
1691 }
1692 /* Note: the address is sufficiently aligned for cast:
1693 * we _copied_ IA-NA, and copy is always well-aligned.
1694 */
1695 requested_ipv6 = (struct in6_addr*) iaaddr->data;
1696 move_from_unaligned32(lease_seconds, iaaddr->data + 16 + 4);
1697 lease_seconds = ntohl(lease_seconds);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001698/// TODO: check for 0 lease time?
Denys Vlasenko14f13202018-01-16 21:47:10 +01001699 bb_error_msg("%s obtained, lease time %u",
1700 "IPv6", /*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds);
Denys Vlasenko7c44b602018-01-17 13:55:51 +01001701 address_timeout = lease_seconds;
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001702 }
1703 if (option_mask32 & OPT_d) {
1704 struct d6_option *iaprefix;
1705
1706 free(client6_data.ia_pd);
1707 client6_data.ia_pd = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_PD);
1708 if (!client6_data.ia_pd) {
1709 bb_error_msg("no %s option, ignoring packet", "IA_PD");
1710 continue;
1711 }
1712 if (client6_data.ia_pd->len < (4 + 4 + 4) + (2 + 2 + 4 + 4 + 1 + 16)) {
Denys Vlasenko14f13202018-01-16 21:47:10 +01001713 bb_error_msg("%s option is too short:%d bytes",
1714 "IA_PD", client6_data.ia_pd->len);
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001715 continue;
1716 }
1717 iaprefix = d6_find_option(client6_data.ia_pd->data + 4 + 4 + 4,
1718 client6_data.ia_pd->data + client6_data.ia_pd->len,
1719 D6_OPT_IAPREFIX
1720 );
1721 if (!iaprefix) {
1722 bb_error_msg("no %s option, ignoring packet", "IAPREFIX");
1723 continue;
1724 }
1725 if (iaprefix->len < (4 + 4 + 1 + 16)) {
Denys Vlasenko14f13202018-01-16 21:47:10 +01001726 bb_error_msg("%s option is too short:%d bytes",
1727 "IAPREFIX", iaprefix->len);
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001728 continue;
1729 }
1730 move_from_unaligned32(lease_seconds, iaprefix->data + 4);
1731 lease_seconds = ntohl(lease_seconds);
Denys Vlasenko14f13202018-01-16 21:47:10 +01001732 bb_error_msg("%s obtained, lease time %u",
1733 "prefix", /*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds);
Denys Vlasenko7c44b602018-01-17 13:55:51 +01001734 prefix_timeout = lease_seconds;
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001735 }
Denys Vlasenko6e9e6d82018-01-16 21:52:23 +01001736 if (!address_timeout)
1737 address_timeout = prefix_timeout;
1738 if (!prefix_timeout)
1739 prefix_timeout = address_timeout;
Denys Vlasenko7c44b602018-01-17 13:55:51 +01001740 /* note: "int timeout" will not overflow even with 0xffffffff inputs here: */
1741 timeout = (prefix_timeout < address_timeout ? prefix_timeout : address_timeout) / 2;
Denys Vlasenko14f13202018-01-16 21:47:10 +01001742 /* paranoia: must not be too small */
1743 if (timeout < 0x10)
1744 timeout = 0x10;
1745 /* enter bound state */
David Decotigny8f48fc02018-05-24 08:30:15 -07001746 d6_run_script(packet.d6_options, packet_end,
1747 (state == REQUESTING ? "bound" : "renew"));
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001748
1749 state = BOUND;
1750 change_listen_mode(LISTEN_NONE);
1751 if (opt & OPT_q) { /* quit after lease */
1752 goto ret0;
1753 }
1754 /* future renew failures should not exit (JM) */
1755 opt &= ~OPT_n;
1756#if BB_MMU /* NOMMU case backgrounded earlier */
1757 if (!(opt & OPT_f)) {
1758 client_background();
1759 /* do not background again! */
1760 opt = ((opt & ~OPT_b) | OPT_f);
1761 }
1762#endif
1763 already_waited_sec = 0;
1764 continue; /* back to main loop */
1765 }
1766 continue;
1767 /* case BOUND: - ignore all packets */
1768 /* case RELEASED: - ignore all packets */
1769 }
1770 /* back to main loop */
1771 } /* for (;;) - main loop ends */
1772
1773 ret0:
1774 if (opt & OPT_R) /* release on quit */
1775 perform_d6_release(&srv6_buf, requested_ipv6);
1776 retval = 0;
1777 ret:
1778 /*if (client_config.pidfile) - remove_pidfile has its own check */
1779 remove_pidfile(client_config.pidfile);
1780 return retval;
1781}