blob: 7f288f891af12e464addf33c3ce27c8945f662bf [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 * Copyright (C) 2011-2017 Denys Vlasenko.
Denys Vlasenko9ba75042011-11-07 15:55:39 +01006 *
7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01009//config:config UDHCPC6
Denys Vlasenkob097a842018-12-28 03:20:17 +010010//config: bool "udhcpc6 (21 kb)"
Denys Vlasenkocc45cbc2019-04-13 17:32:40 +020011//config: default y
Mike Frysinger3da46c82012-05-02 21:45:35 -040012//config: depends on FEATURE_IPV6
13//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020014//config: udhcpc6 is a DHCPv6 client
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +020015//config:
16//config:config FEATURE_UDHCPC6_RFC3646
17//config: bool "Support RFC 3646 (DNS server and search list)"
18//config: default y
19//config: depends on UDHCPC6
20//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020021//config: List of DNS servers and domain search list can be requested with
22//config: "-O dns" and "-O search". If server gives these values,
23//config: they will be set in environment variables "dns" and "search".
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +020024//config:
25//config:config FEATURE_UDHCPC6_RFC4704
26//config: bool "Support RFC 4704 (Client FQDN)"
27//config: default y
28//config: depends on UDHCPC6
29//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020030//config: You can request FQDN to be given by server using "-O fqdn".
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +020031//config:
32//config:config FEATURE_UDHCPC6_RFC4833
33//config: bool "Support RFC 4833 (Timezones)"
34//config: default y
35//config: depends on UDHCPC6
36//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020037//config: You can request POSIX timezone with "-O tz" and timezone name
38//config: with "-O timezone".
Samuel Mendoza-Jonas23cbd7d2018-05-14 14:29:12 +100039//config:
40//config:config FEATURE_UDHCPC6_RFC5970
41//config: bool "Support RFC 5970 (Network Boot)"
42//config: default y
43//config: depends on UDHCPC6
44//config: help
45//config: You can request bootfile-url with "-O bootfile_url" and
46//config: bootfile-params with "-O bootfile_params".
Denys Vlasenko9ba75042011-11-07 15:55:39 +010047
48//applet:IF_UDHCPC6(APPLET(udhcpc6, BB_DIR_USR_BIN, BB_SUID_DROP))
49
Denys Vlasenko8cab6672012-04-20 14:48:00 +020050//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 +020051//kbuild:lib-$(CONFIG_FEATURE_UDHCPC6_RFC3646) += domain_codec.o
52//kbuild:lib-$(CONFIG_FEATURE_UDHCPC6_RFC4704) += domain_codec.o
Denys Vlasenko9ba75042011-11-07 15:55:39 +010053
54#include <syslog.h>
55/* Override ENABLE_FEATURE_PIDFILE - ifupdown needs our pidfile to always exist */
56#define WANT_PIDFILE 1
57#include "common.h"
58#include "dhcpd.h"
59#include "dhcpc.h"
60#include "d6_common.h"
61
62#include <netinet/if_ether.h>
63#include <netpacket/packet.h>
64#include <linux/filter.h>
65
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +020066/* "struct client_data_t client_data" is in bb_common_bufsiz1 */
Denys Vlasenko9ba75042011-11-07 15:55:39 +010067
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +020068static const struct dhcp_optflag d6_optflags[] = {
69#if ENABLE_FEATURE_UDHCPC6_RFC3646
70 { OPTION_6RD | OPTION_LIST | OPTION_REQ, D6_OPT_DNS_SERVERS },
71 { OPTION_DNS_STRING | OPTION_LIST | OPTION_REQ, D6_OPT_DOMAIN_LIST },
72#endif
73#if ENABLE_FEATURE_UDHCPC6_RFC4704
74 { OPTION_DNS_STRING, D6_OPT_CLIENT_FQDN },
75#endif
76#if ENABLE_FEATURE_UDHCPC6_RFC4833
77 { OPTION_STRING, D6_OPT_TZ_POSIX },
78 { OPTION_STRING, D6_OPT_TZ_NAME },
79#endif
Samuel Mendoza-Jonas23cbd7d2018-05-14 14:29:12 +100080#if ENABLE_FEATURE_UDHCPC6_RFC5970
81 { OPTION_STRING, D6_OPT_BOOT_URL },
82 { OPTION_STRING, D6_OPT_BOOT_PARAM },
83#endif
Samuel Mendoza-Jonas30f4d522018-05-14 14:29:13 +100084 { OPTION_STRING, 0xd1 }, /* DHCP_PXE_CONF_FILE */
85 { OPTION_STRING, 0xd2 }, /* DHCP_PXE_PATH_PREFIX */
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +020086 { 0, 0 }
87};
88/* Must match d6_optflags[] order */
89static const char d6_option_strings[] ALIGN1 =
90#if ENABLE_FEATURE_UDHCPC6_RFC3646
91 "dns" "\0" /* D6_OPT_DNS_SERVERS */
92 "search" "\0" /* D6_OPT_DOMAIN_LIST */
93#endif
94#if ENABLE_FEATURE_UDHCPC6_RFC4704
95 "fqdn" "\0" /* D6_OPT_CLIENT_FQDN */
96#endif
97#if ENABLE_FEATURE_UDHCPC6_RFC4833
98 "tz" "\0" /* D6_OPT_TZ_POSIX */
99 "timezone" "\0" /* D6_OPT_TZ_NAME */
100#endif
Samuel Mendoza-Jonas23cbd7d2018-05-14 14:29:12 +1000101#if ENABLE_FEATURE_UDHCPC6_RFC5970
102 "bootfile_url" "\0" /* D6_OPT_BOOT_URL */
103 "bootfile_param" "\0" /* D6_OPT_BOOT_PARAM */
104#endif
Samuel Mendoza-Jonas30f4d522018-05-14 14:29:13 +1000105 "pxeconffile" "\0" /* DHCP_PXE_CONF_FILE */
106 "pxepathprefix" "\0" /* DHCP_PXE_PATH_PREFIX */
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200107 "\0";
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100108
109#if ENABLE_LONG_OPTS
Denys Vlasenko7e21f042011-11-08 11:39:41 +0100110static const char udhcpc6_longopts[] ALIGN1 =
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100111 "interface\0" Required_argument "i"
112 "now\0" No_argument "n"
113 "pidfile\0" Required_argument "p"
114 "quit\0" No_argument "q"
115 "release\0" No_argument "R"
116 "request\0" Required_argument "r"
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100117 "requestprefix\0" No_argument "d"
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100118 "script\0" Required_argument "s"
119 "timeout\0" Required_argument "T"
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100120 "retries\0" Required_argument "t"
121 "tryagain\0" Required_argument "A"
122 "syslog\0" No_argument "S"
123 "request-option\0" Required_argument "O"
124 "no-default-options\0" No_argument "o"
125 "foreground\0" No_argument "f"
Eivind Versvik22a63392019-08-24 17:23:48 +0200126 "stateless\0" No_argument "l"
Denys Vlasenkoed820cc2017-05-08 15:11:02 +0200127 USE_FOR_MMU(
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100128 "background\0" No_argument "b"
Denys Vlasenkoed820cc2017-05-08 15:11:02 +0200129 )
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100130/// IF_FEATURE_UDHCPC_ARPING("arping\0" No_argument "a")
131 IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P")
132 ;
133#endif
134/* Must match getopt32 option string order */
135enum {
136 OPT_i = 1 << 0,
137 OPT_n = 1 << 1,
138 OPT_p = 1 << 2,
139 OPT_q = 1 << 3,
140 OPT_R = 1 << 4,
141 OPT_r = 1 << 5,
142 OPT_s = 1 << 6,
143 OPT_T = 1 << 7,
144 OPT_t = 1 << 8,
145 OPT_S = 1 << 9,
146 OPT_A = 1 << 10,
147 OPT_O = 1 << 11,
148 OPT_o = 1 << 12,
149 OPT_x = 1 << 13,
150 OPT_f = 1 << 14,
Eivind Versvik22a63392019-08-24 17:23:48 +0200151 OPT_l = 1 << 15,
152 OPT_d = 1 << 16,
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100153/* The rest has variable bit positions, need to be clever */
Eivind Versvik22a63392019-08-24 17:23:48 +0200154 OPTBIT_d = 16,
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/*** Script execution code ***/
210
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100211static char** new_env(void)
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100212{
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100213 client6_data.env_ptr = xrealloc_vector(client6_data.env_ptr, 3, client6_data.env_idx);
214 return &client6_data.env_ptr[client6_data.env_idx++];
215}
216
David Decotigny8f48fc02018-05-24 08:30:15 -0700217static char *string_option_to_env(const uint8_t *option,
218 const uint8_t *option_end)
Samuel Mendoza-Jonasbcdec1a2018-05-14 14:29:11 +1000219{
220 const char *ptr, *name = NULL;
221 unsigned val_len;
222 int i;
223
224 ptr = d6_option_strings;
225 i = 0;
226 while (*ptr) {
227 if (d6_optflags[i].code == option[1]) {
228 name = ptr;
229 goto found;
230 }
231 ptr += strlen(ptr) + 1;
232 i++;
233 }
234 bb_error_msg("can't find option name for 0x%x, skipping", option[1]);
235 return NULL;
236
237 found:
238 val_len = (option[2] << 8) | option[3];
239 if (val_len + &option[D6_OPT_DATA] > option_end) {
James Byrne69374872019-07-02 11:35:03 +0200240 bb_simple_error_msg("option data exceeds option length");
Samuel Mendoza-Jonasbcdec1a2018-05-14 14:29:11 +1000241 return NULL;
242 }
243 return xasprintf("%s=%.*s", name, val_len, (char*)option + 4);
244}
245
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100246/* put all the parameters into the environment */
David Decotigny8f48fc02018-05-24 08:30:15 -0700247static void option_to_env(const uint8_t *option, const uint8_t *option_end)
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100248{
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200249#if ENABLE_FEATURE_UDHCPC6_RFC3646
250 int addrs, option_offset;
251#endif
Denys Vlasenkoab030612017-03-27 22:49:12 +0200252 /* "length minus 4" */
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100253 int len_m4 = option_end - option - 4;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200254
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100255 while (len_m4 >= 0) {
256 uint32_t v32;
257 char ipv6str[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")];
258
259 if (option[0] != 0 || option[2] != 0)
260 break;
261
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200262 /* Check if option-length exceeds size of option */
263 if (option[3] > len_m4)
264 break;
265
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100266 switch (option[1]) {
267 //case D6_OPT_CLIENTID:
268 //case D6_OPT_SERVERID:
269 case D6_OPT_IA_NA:
270 case D6_OPT_IA_PD:
271 option_to_env(option + 16, option + 4 + option[3]);
272 break;
273 //case D6_OPT_IA_TA:
274 case D6_OPT_IAADDR:
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200275/* 0 1 2 3
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100276 * 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
277 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
278 * | OPTION_IAADDR | option-len |
279 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
280 * | |
281 * | IPv6 address |
282 * | |
283 * | |
284 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
285 * | preferred-lifetime |
286 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
287 * | valid-lifetime |
288 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
289 */
David Decotignya174c792018-05-24 08:30:16 -0700290 /* Make sure payload contains an address */
291 if (option[3] < 24)
292 break;
293
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100294 sprint_nip6(ipv6str, option + 4);
295 *new_env() = xasprintf("ipv6=%s", ipv6str);
296
297 move_from_unaligned32(v32, option + 4 + 16 + 4);
298 *new_env() = xasprintf("lease=%u", (unsigned)v32);
299 break;
300
301 //case D6_OPT_ORO:
302 //case D6_OPT_PREFERENCE:
303 //case D6_OPT_ELAPSED_TIME:
304 //case D6_OPT_RELAY_MSG:
305 //case D6_OPT_AUTH:
306 //case D6_OPT_UNICAST:
307 //case D6_OPT_STATUS_CODE:
308 //case D6_OPT_RAPID_COMMIT:
309 //case D6_OPT_USER_CLASS:
310 //case D6_OPT_VENDOR_CLASS:
311 //case D6_OPT_VENDOR_OPTS:
312 //case D6_OPT_INTERFACE_ID:
313 //case D6_OPT_RECONF_MSG:
314 //case D6_OPT_RECONF_ACCEPT:
315
316 case D6_OPT_IAPREFIX:
317/* 0 1 2 3
318 * 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
319 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
320 * | OPTION_IAPREFIX | option-length |
321 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
322 * | preferred-lifetime |
323 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
324 * | valid-lifetime |
325 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
326 * | prefix-length | |
327 * +-+-+-+-+-+-+-+-+ IPv6 prefix |
328 * | (16 octets) |
329 * | |
330 * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
331 * | |
332 * +-+-+-+-+-+-+-+-+
333 */
Denys Vlasenko688cb3b2018-01-16 16:00:13 +0100334 move_from_unaligned32(v32, option + 4 + 4);
335 *new_env() = xasprintf("ipv6prefix_lease=%u", (unsigned)v32);
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100336
Denys Vlasenko688cb3b2018-01-16 16:00:13 +0100337 sprint_nip6(ipv6str, option + 4 + 4 + 4 + 1);
338 *new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4 + 4]));
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200339 break;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200340#if ENABLE_FEATURE_UDHCPC6_RFC3646
341 case D6_OPT_DNS_SERVERS: {
342 char *dlist;
343
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200344 /* Make sure payload-size is a multiple of 16 */
345 if ((option[3] & 0x0f) != 0)
346 break;
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200347
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200348 /* Get the number of addresses on the option */
349 addrs = option[3] >> 4;
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200350
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200351 /* Setup environment variable */
352 *new_env() = dlist = xmalloc(4 + addrs * 40 - 1);
353 dlist = stpcpy(dlist, "dns=");
354 option_offset = 0;
355
356 while (addrs--) {
357 sprint_nip6(dlist, option + 4 + option_offset);
358 dlist += 39;
359 option_offset += 16;
360 if (addrs)
361 *dlist++ = ' ';
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200362 }
363
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200364 break;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200365 }
366 case D6_OPT_DOMAIN_LIST: {
367 char *dlist;
368
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200369 dlist = dname_dec(option + 4, (option[2] << 8) | option[3], "search=");
370 if (!dlist)
371 break;
372 *new_env() = dlist;
373 break;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200374 }
375#endif
376#if ENABLE_FEATURE_UDHCPC6_RFC4704
377 case D6_OPT_CLIENT_FQDN: {
378 char *dlist;
379
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200380 if (option[3] == 0)
381 break;
382 /* Work around broken ISC DHCPD6.
383 * ISC DHCPD6 does not implement RFC 4704 correctly: It says the first
384 * byte of option-payload should contain flags where the bits 7-3 are
385 * reserved for future use and MUST be zero. Instead ISC DHCPD6 just
386 * writes the entire FQDN as string to option-payload. We assume a
387 * broken server here if any of the reserved bits are set.
388 */
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200389 if (option[4] & 0xf8) {
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200390 *new_env() = xasprintf("fqdn=%.*s", (int)option[3], (char*)option + 4);
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200391 break;
392 }
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200393 dlist = dname_dec(option + 5, (/*(option[2] << 8) |*/ option[3]) - 1, "fqdn=");
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200394 if (!dlist)
395 break;
396 *new_env() = dlist;
397 break;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200398 }
399#endif
400#if ENABLE_FEATURE_UDHCPC6_RFC4833
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200401 /* RFC 4833 Timezones */
402 case D6_OPT_TZ_POSIX:
403 *new_env() = xasprintf("tz=%.*s", (int)option[3], (char*)option + 4);
404 break;
405 case D6_OPT_TZ_NAME:
406 *new_env() = xasprintf("tz_name=%.*s", (int)option[3], (char*)option + 4);
407 break;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200408#endif
Samuel Mendoza-Jonas23cbd7d2018-05-14 14:29:12 +1000409 case D6_OPT_BOOT_URL:
410 case D6_OPT_BOOT_PARAM:
Samuel Mendoza-Jonas30f4d522018-05-14 14:29:13 +1000411 case 0xd1: /* DHCP_PXE_CONF_FILE */
412 case 0xd2: /* DHCP_PXE_PATH_PREFIX */
Samuel Mendoza-Jonas23cbd7d2018-05-14 14:29:12 +1000413 {
414 char *tmp = string_option_to_env(option, option_end);
415 if (tmp)
416 *new_env() = tmp;
417 break;
418 }
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100419 }
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100420 len_m4 -= 4 + option[3];
Denys Vlasenko64d58aa2017-03-27 22:22:09 +0200421 option += 4 + option[3];
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100422 }
423}
424
David Decotigny8f48fc02018-05-24 08:30:15 -0700425static char **fill_envp(const uint8_t *option, const uint8_t *option_end)
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100426{
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100427 char **envp, **curr;
428
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100429 client6_data.env_ptr = NULL;
430 client6_data.env_idx = 0;
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100431
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +0200432 *new_env() = xasprintf("interface=%s", client_data.interface);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100433
David Decotigny8f48fc02018-05-24 08:30:15 -0700434 if (option)
435 option_to_env(option, option_end);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100436
Denys Vlasenkoa092a892011-11-16 20:17:12 +0100437 envp = curr = client6_data.env_ptr;
438 while (*curr)
439 putenv(*curr++);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100440
441 return envp;
442}
443
Denys Vlasenkof6def872021-06-03 16:14:04 +0200444/* Call a script with env vars */
David Decotigny8f48fc02018-05-24 08:30:15 -0700445static void d6_run_script(const uint8_t *option, const uint8_t *option_end,
446 const char *name)
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100447{
448 char **envp, **curr;
449 char *argv[3];
450
David Decotigny8f48fc02018-05-24 08:30:15 -0700451 envp = fill_envp(option, option_end);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100452
453 /* call script */
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +0200454 log1("executing %s %s", client_data.script, name);
455 argv[0] = (char*) client_data.script;
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100456 argv[1] = (char*) name;
457 argv[2] = NULL;
458 spawn_and_wait(argv);
459
460 for (curr = envp; *curr; curr++) {
461 log2(" %s", *curr);
462 bb_unsetenv_and_free(*curr);
463 }
464 free(envp);
465}
466
Denys Vlasenkof6def872021-06-03 16:14:04 +0200467/* Call a script with no env var */
David Decotigny8f48fc02018-05-24 08:30:15 -0700468static void d6_run_script_no_option(const char *name)
469{
470 d6_run_script(NULL, NULL, name);
471}
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100472
473/*** Sending/receiving packets ***/
474
475static ALWAYS_INLINE uint32_t random_xid(void)
476{
477 uint32_t t = rand() & htonl(0x00ffffff);
478 return t;
479}
480
481/* Initialize the packet with the proper defaults */
482static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid)
483{
Denys Vlasenko11e024a2019-09-24 14:01:00 +0200484 uint8_t *ptr;
Denys Vlasenko11e024a2019-09-24 14:01:00 +0200485 unsigned secs;
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100486
487 memset(packet, 0, sizeof(*packet));
488
489 packet->d6_xid32 = xid;
490 packet->d6_msg_type = type;
491
Denys Vlasenko11e024a2019-09-24 14:01:00 +0200492 /* ELAPSED_TIME option is required to be present by the RFC,
493 * and some servers do check for its presense. [which?]
494 */
495 ptr = packet->d6_options; /* NB: it is 32-bit aligned */
496 *((uint32_t*)ptr) = htonl((D6_OPT_ELAPSED_TIME << 16) + 2);
497 ptr += 4;
498 client_data.last_secs = monotonic_sec();
499 if (client_data.first_secs == 0)
500 client_data.first_secs = client_data.last_secs;
501 secs = client_data.last_secs - client_data.first_secs;
502 *((uint16_t*)ptr) = (secs < 0xffff) ? htons(secs) : 0xffff;
503 ptr += 2;
504
Denys Vlasenko265fcdd2021-06-02 13:50:26 +0200505 return ptr;
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100506}
507
508static uint8_t *add_d6_client_options(uint8_t *ptr)
509{
Denys Vlasenko60275972018-05-14 11:06:35 +0200510 struct option_set *curr;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200511 uint8_t *start = ptr;
512 unsigned option;
Denys Vlasenko60275972018-05-14 11:06:35 +0200513 uint16_t len;
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100514
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200515 ptr += 4;
516 for (option = 1; option < 256; option++) {
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +0200517 if (client_data.opt_mask[option >> 3] & (1 << (option & 7))) {
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200518 ptr[0] = (option >> 8);
519 ptr[1] = option;
520 ptr += 2;
521 }
522 }
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100523
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200524 if ((ptr - start - 4) != 0) {
525 start[0] = (D6_OPT_ORO >> 8);
526 start[1] = D6_OPT_ORO;
527 start[2] = ((ptr - start - 4) >> 8);
528 start[3] = (ptr - start - 4);
529 } else
530 ptr = start;
531
532#if ENABLE_FEATURE_UDHCPC6_RFC4704
533 ptr = mempcpy(ptr, &opt_fqdn_req, sizeof(opt_fqdn_req));
534#endif
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100535 /* Add -x options if any */
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +0200536 curr = client_data.options;
Denys Vlasenko60275972018-05-14 11:06:35 +0200537 while (curr) {
538 len = (curr->data[D6_OPT_LEN] << 8) | curr->data[D6_OPT_LEN + 1];
539 ptr = mempcpy(ptr, curr->data, D6_OPT_DATA + len);
540 curr = curr->next;
541 }
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +0200542
543 return ptr;
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100544}
545
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +0200546static int d6_mcast_from_client_data_ifindex(struct d6_packet *packet, uint8_t *end)
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100547{
Denys Vlasenko0d75e8b2019-04-13 19:43:15 +0200548 /* FF02::1:2 is "All_DHCP_Relay_Agents_and_Servers" address */
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100549 static const uint8_t FF02__1_2[16] = {
550 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
551 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
552 };
Uwe Glaeserfaab9062020-06-21 02:56:12 +0200553 /* IPv6 requires different multicast contents in Ethernet Frame (RFC 2464) */
554 static const uint8_t MAC_DHCP6MCAST_ADDR[6] ALIGN2 = {
555 0x33, 0x33, 0x00, 0x01, 0x00, 0x02,
556 };
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100557
Denys Vlasenkof3d67112020-12-15 21:55:15 +0100558 return d6_send_raw_packet_from_client_data_ifindex(
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100559 packet, (end - (uint8_t*) packet),
Denys Vlasenkoe09f5e32017-03-27 22:10:15 +0200560 /*src*/ &client6_data.ll_ip6, CLIENT_PORT6,
Denys Vlasenkof3d67112020-12-15 21:55:15 +0100561 /*dst*/ (struct in6_addr*)FF02__1_2, SERVER_PORT6, MAC_DHCP6MCAST_ADDR
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100562 );
563}
564
Eivind Versvik22a63392019-08-24 17:23:48 +0200565/* RFC 3315 18.1.5. Creation and Transmission of Information-request Messages
566 *
567 * The client uses an Information-request message to obtain
568 * configuration information without having addresses assigned to it.
569 *
570 * The client sets the "msg-type" field to INFORMATION-REQUEST. The
571 * client generates a transaction ID and inserts this value in the
572 * "transaction-id" field.
573 *
574 * The client SHOULD include a Client Identifier option to identify
575 * itself to the server. If the client does not include a Client
576 * Identifier option, the server will not be able to return any client-
577 * specific options to the client, or the server may choose not to
578 * respond to the message at all. The client MUST include a Client
579 * Identifier option if the Information-Request message will be
580 * authenticated.
581 *
582 * The client MUST include an Option Request option (see section 22.7)
583 * to indicate the options the client is interested in receiving. The
584 * client MAY include options with data values as hints to the server
585 * about parameter values the client would like to have returned.
586 */
587/* NOINLINE: limit stack usage in caller */
588static NOINLINE int send_d6_info_request(uint32_t xid)
589{
590 struct d6_packet packet;
591 uint8_t *opt_ptr;
592
Denys Vlasenko265fcdd2021-06-02 13:50:26 +0200593 /* Fill in: msg type */
Eivind Versvik22a63392019-08-24 17:23:48 +0200594 opt_ptr = init_d6_packet(&packet, D6_MSG_INFORMATION_REQUEST, xid);
595
Denys Vlasenko265fcdd2021-06-02 13:50:26 +0200596 /* Add options: client-id,
Eivind Versvik22a63392019-08-24 17:23:48 +0200597 * "param req" option according to -O, options specified with -x
598 */
599 opt_ptr = add_d6_client_options(opt_ptr);
600
601 bb_error_msg("sending %s", "info request");
602 return d6_mcast_from_client_data_ifindex(&packet, opt_ptr);
603}
604
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100605/* Milticast a DHCPv6 Solicit packet to the network, with an optionally requested IP.
606 *
607 * RFC 3315 17.1.1. Creation of Solicit Messages
608 *
609 * The client MUST include a Client Identifier option to identify itself
610 * to the server. The client includes IA options for any IAs to which
611 * it wants the server to assign addresses. The client MAY include
612 * addresses in the IAs as a hint to the server about addresses for
613 * which the client has a preference. ...
614 *
615 * The client uses IA_NA options to request the assignment of non-
616 * temporary addresses and uses IA_TA options to request the assignment
617 * of temporary addresses. Either IA_NA or IA_TA options, or a
618 * combination of both, can be included in DHCP messages.
619 *
620 * The client SHOULD include an Option Request option (see section 22.7)
621 * to indicate the options the client is interested in receiving. The
622 * client MAY additionally include instances of those options that are
623 * identified in the Option Request option, with data values as hints to
624 * the server about parameter values the client would like to have
625 * returned.
626 *
627 * The client includes a Reconfigure Accept option (see section 22.20)
628 * if the client is willing to accept Reconfigure messages from the
629 * server.
630 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
631 | OPTION_CLIENTID | option-len |
632 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
633 . .
634 . DUID .
635 . (variable length) .
636 . .
637 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
638
639
640 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
641 | OPTION_IA_NA | option-len |
642 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
643 | IAID (4 octets) |
644 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
645 | T1 |
646 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
647 | T2 |
648 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
649 | |
650 . IA_NA-options .
651 . .
652 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
653
654
655 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
656 | OPTION_IAADDR | option-len |
657 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
658 | |
659 | IPv6 address |
660 | |
661 | |
662 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
663 | preferred-lifetime |
664 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
665 | valid-lifetime |
666 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
667 . .
668 . IAaddr-options .
669 . .
670 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
671
672
673 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
674 | OPTION_ORO | option-len |
675 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
676 | requested-option-code-1 | requested-option-code-2 |
677 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
678 | ... |
679 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
680
681
682 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
683 | OPTION_RECONF_ACCEPT | 0 |
684 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
685 */
686/* NOINLINE: limit stack usage in caller */
687static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ipv6)
688{
689 struct d6_packet packet;
690 uint8_t *opt_ptr;
691 unsigned len;
692
Denys Vlasenko265fcdd2021-06-02 13:50:26 +0200693 /* Fill in: msg type */
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100694 opt_ptr = init_d6_packet(&packet, D6_MSG_SOLICIT, xid);
695
696 /* Create new IA_NA, optionally with included IAADDR with requested IP */
697 free(client6_data.ia_na);
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100698 client6_data.ia_na = NULL;
699 if (option_mask32 & OPT_r) {
700 len = requested_ipv6 ? 2+2+4+4+4 + 2+2+16+4+4 : 2+2+4+4+4;
701 client6_data.ia_na = xzalloc(len);
702 client6_data.ia_na->code = D6_OPT_IA_NA;
703 client6_data.ia_na->len = len - 4;
Denys Vlasenko72f12ac2019-06-11 18:18:48 +0200704 *(bb__aliased_uint32_t*)client6_data.ia_na->data = rand(); /* IAID */
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100705 if (requested_ipv6) {
706 struct d6_option *iaaddr = (void*)(client6_data.ia_na->data + 4+4+4);
707 iaaddr->code = D6_OPT_IAADDR;
708 iaaddr->len = 16+4+4;
709 memcpy(iaaddr->data, requested_ipv6, 16);
710 }
711 opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, len);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100712 }
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100713
714 /* IA_PD */
715 free(client6_data.ia_pd);
716 client6_data.ia_pd = NULL;
717 if (option_mask32 & OPT_d) {
718 len = 2+2+4+4+4;
719 client6_data.ia_pd = xzalloc(len);
720 client6_data.ia_pd->code = D6_OPT_IA_PD;
721 client6_data.ia_pd->len = len - 4;
Denys Vlasenko72f12ac2019-06-11 18:18:48 +0200722 *(bb__aliased_uint32_t*)client6_data.ia_pd->data = rand(); /* IAID */
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100723 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, len);
724 }
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100725
Denys Vlasenko265fcdd2021-06-02 13:50:26 +0200726 /* Add options: client-id,
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100727 * "param req" option according to -O, options specified with -x
728 */
729 opt_ptr = add_d6_client_options(opt_ptr);
730
James Byrne253c4e72019-04-12 17:01:51 +0000731 bb_info_msg("sending %s", "discover");
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +0200732 return d6_mcast_from_client_data_ifindex(&packet, opt_ptr);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100733}
734
735/* Multicast a DHCPv6 request message
736 *
737 * RFC 3315 18.1.1. Creation and Transmission of Request Messages
738 *
739 * The client uses a Request message to populate IAs with addresses and
740 * obtain other configuration information. The client includes one or
741 * more IA options in the Request message. The server then returns
742 * addresses and other information about the IAs to the client in IA
743 * options in a Reply message.
744 *
745 * The client generates a transaction ID and inserts this value in the
746 * "transaction-id" field.
747 *
748 * The client places the identifier of the destination server in a
749 * Server Identifier option.
750 *
751 * The client MUST include a Client Identifier option to identify itself
752 * to the server. The client adds any other appropriate options,
753 * including one or more IA options (if the client is requesting that
754 * the server assign it some network addresses).
755 *
756 * The client MUST include an Option Request option (see section 22.7)
757 * to indicate the options the client is interested in receiving. The
758 * client MAY include options with data values as hints to the server
759 * about parameter values the client would like to have returned.
760 *
761 * The client includes a Reconfigure Accept option (see section 22.20)
762 * indicating whether or not the client is willing to accept Reconfigure
763 * messages from the server.
764 */
765/* NOINLINE: limit stack usage in caller */
766static NOINLINE int send_d6_select(uint32_t xid)
767{
768 struct d6_packet packet;
769 uint8_t *opt_ptr;
770
Denys Vlasenko265fcdd2021-06-02 13:50:26 +0200771 /* Fill in: msg type */
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100772 opt_ptr = init_d6_packet(&packet, D6_MSG_REQUEST, xid);
773
774 /* server id */
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200775 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100776 /* IA NA (contains requested IP) */
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100777 if (client6_data.ia_na)
778 opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
779 /* IA PD */
780 if (client6_data.ia_pd)
781 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100782
Denys Vlasenko265fcdd2021-06-02 13:50:26 +0200783 /* Add options: client-id,
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100784 * "param req" option according to -O, options specified with -x
785 */
786 opt_ptr = add_d6_client_options(opt_ptr);
787
James Byrne253c4e72019-04-12 17:01:51 +0000788 bb_info_msg("sending %s", "select");
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +0200789 return d6_mcast_from_client_data_ifindex(&packet, opt_ptr);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100790}
791
792/* Unicast or broadcast a DHCP renew message
793 *
794 * RFC 3315 18.1.3. Creation and Transmission of Renew Messages
795 *
796 * To extend the valid and preferred lifetimes for the addresses
797 * associated with an IA, the client sends a Renew message to the server
798 * from which the client obtained the addresses in the IA containing an
799 * IA option for the IA. The client includes IA Address options in the
800 * IA option for the addresses associated with the IA. The server
801 * determines new lifetimes for the addresses in the IA according to the
802 * administrative configuration of the server. The server may also add
803 * new addresses to the IA. The server may remove addresses from the IA
804 * by setting the preferred and valid lifetimes of those addresses to
805 * zero.
806 *
807 * The server controls the time at which the client contacts the server
808 * to extend the lifetimes on assigned addresses through the T1 and T2
809 * parameters assigned to an IA.
810 *
811 * At time T1 for an IA, the client initiates a Renew/Reply message
812 * exchange to extend the lifetimes on any addresses in the IA. The
813 * client includes an IA option with all addresses currently assigned to
814 * the IA in its Renew message.
815 *
816 * If T1 or T2 is set to 0 by the server (for an IA_NA) or there are no
817 * T1 or T2 times (for an IA_TA), the client may send a Renew or Rebind
818 * message, respectively, at the client's discretion.
819 *
820 * The client sets the "msg-type" field to RENEW. The client generates
821 * a transaction ID and inserts this value in the "transaction-id"
822 * field.
823 *
824 * The client places the identifier of the destination server in a
825 * Server Identifier option.
826 *
827 * The client MUST include a Client Identifier option to identify itself
828 * to the server. The client adds any appropriate options, including
829 * one or more IA options. The client MUST include the list of
830 * addresses the client currently has associated with the IAs in the
831 * Renew message.
832 *
833 * The client MUST include an Option Request option (see section 22.7)
834 * to indicate the options the client is interested in receiving. The
835 * client MAY include options with data values as hints to the server
836 * about parameter values the client would like to have returned.
837 */
838/* NOINLINE: limit stack usage in caller */
839static NOINLINE int send_d6_renew(uint32_t xid, struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
840{
841 struct d6_packet packet;
842 uint8_t *opt_ptr;
843
Denys Vlasenko265fcdd2021-06-02 13:50:26 +0200844 /* Fill in: msg type */
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100845 opt_ptr = init_d6_packet(&packet, DHCPREQUEST, xid);
846
847 /* server id */
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200848 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100849 /* IA NA (contains requested IP) */
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100850 if (client6_data.ia_na)
851 opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
852 /* IA PD */
853 if (client6_data.ia_pd)
854 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100855
Denys Vlasenko265fcdd2021-06-02 13:50:26 +0200856 /* Add options: client-id,
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100857 * "param req" option according to -O, options specified with -x
858 */
859 opt_ptr = add_d6_client_options(opt_ptr);
860
James Byrne253c4e72019-04-12 17:01:51 +0000861 bb_info_msg("sending %s", "renew");
Denys Vlasenko148788e2018-06-21 17:36:22 +0200862 if (server_ipv6)
Denys Vlasenkof3d67112020-12-15 21:55:15 +0100863 return d6_send_kernel_packet_from_client_data_ifindex(
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100864 &packet, (opt_ptr - (uint8_t*) &packet),
Denys Vlasenko04ac6e02013-01-28 15:25:35 +0100865 our_cur_ipv6, CLIENT_PORT6,
Denys Vlasenkof3d67112020-12-15 21:55:15 +0100866 server_ipv6, SERVER_PORT6
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100867 );
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +0200868 return d6_mcast_from_client_data_ifindex(&packet, opt_ptr);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100869}
870
871/* Unicast a DHCP release message */
Denys Vlasenko9d05ad02018-11-03 23:34:03 +0100872static
873ALWAYS_INLINE /* one caller, help compiler to use this fact */
874int send_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100875{
876 struct d6_packet packet;
877 uint8_t *opt_ptr;
Denys Vlasenko265fcdd2021-06-02 13:50:26 +0200878 struct option_set *ci;
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100879
880 /* Fill in: msg type, client id */
881 opt_ptr = init_d6_packet(&packet, D6_MSG_RELEASE, random_xid());
882 /* server id */
Denys Vlasenko234b82c2017-06-26 19:42:48 +0200883 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100884 /* IA NA (contains our current IP) */
Denys Vlasenkoef5207f2018-01-16 21:39:14 +0100885 if (client6_data.ia_na)
886 opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
887 /* IA PD */
888 if (client6_data.ia_pd)
889 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
Denys Vlasenko265fcdd2021-06-02 13:50:26 +0200890 /* Client-id */
891 ci = udhcp_find_option(client_data.options, D6_OPT_CLIENTID);
892 if (ci)
893 opt_ptr = mempcpy(opt_ptr, ci->data, D6_OPT_DATA + 2+2 + 6);
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100894
James Byrne253c4e72019-04-12 17:01:51 +0000895 bb_info_msg("sending %s", "release");
Denys Vlasenkof3d67112020-12-15 21:55:15 +0100896 return d6_send_kernel_packet_from_client_data_ifindex(
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100897 &packet, (opt_ptr - (uint8_t*) &packet),
Denys Vlasenko04ac6e02013-01-28 15:25:35 +0100898 our_cur_ipv6, CLIENT_PORT6,
Denys Vlasenkof3d67112020-12-15 21:55:15 +0100899 server_ipv6, SERVER_PORT6
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100900 );
901}
902
903/* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */
904/* NOINLINE: limit stack usage in caller */
Denys Vlasenkoed898ed2017-03-27 22:32:44 +0200905static 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 +0100906{
907 int bytes;
908 struct ip6_udp_d6_packet packet;
909
910 bytes = safe_read(fd, &packet, sizeof(packet));
911 if (bytes < 0) {
James Byrne69374872019-07-02 11:35:03 +0200912 log1s("packet read error, ignoring");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100913 /* NB: possible down interface, etc. Caller should pause. */
914 return bytes; /* returns -1 */
915 }
916
917 if (bytes < (int) (sizeof(packet.ip6) + sizeof(packet.udp))) {
James Byrne69374872019-07-02 11:35:03 +0200918 log1s("packet is too short, ignoring");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100919 return -2;
920 }
921
922 if (bytes < sizeof(packet.ip6) + ntohs(packet.ip6.ip6_plen)) {
923 /* packet is bigger than sizeof(packet), we did partial read */
James Byrne69374872019-07-02 11:35:03 +0200924 log1s("oversized packet, ignoring");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100925 return -2;
926 }
927
928 /* ignore any extra garbage bytes */
929 bytes = sizeof(packet.ip6) + ntohs(packet.ip6.ip6_plen);
930
931 /* make sure its the right packet for us, and that it passes sanity checks */
932 if (packet.ip6.ip6_nxt != IPPROTO_UDP
933 || (packet.ip6.ip6_vfc >> 4) != 6
Denys Vlasenko04ac6e02013-01-28 15:25:35 +0100934 || packet.udp.dest != htons(CLIENT_PORT6)
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100935 /* || bytes > (int) sizeof(packet) - can't happen */
936 || packet.udp.len != packet.ip6.ip6_plen
937 ) {
James Byrne69374872019-07-02 11:35:03 +0200938 log1s("unrelated/bogus packet, ignoring");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100939 return -2;
940 }
941
942//How to do this for ipv6?
943// /* verify UDP checksum. IP header has to be modified for this */
944// memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
945// /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
946// packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
947// check = packet.udp.check;
948// packet.udp.check = 0;
Denys Vlasenko4a0eb032020-10-01 03:07:22 +0200949// if (check && check != inet_cksum(&packet, bytes)) {
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +0200950// log1("packet with bad UDP checksum received, ignoring");
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100951// return -2;
952// }
953
Denys Vlasenkoed898ed2017-03-27 22:32:44 +0200954 if (peer_ipv6)
955 *peer_ipv6 = packet.ip6.ip6_src; /* struct copy */
956
Denys Vlasenko4bbc3912021-06-02 19:51:52 +0200957 log2("received %s", "a packet");
958 /* log2 because more informative msg for valid packets is printed later at log1 level */
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100959 d6_dump_packet(&packet.data);
960
961 bytes -= sizeof(packet.ip6) + sizeof(packet.udp);
962 memcpy(d6_pkt, &packet.data, bytes);
963 return bytes;
964}
965
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100966/*** Main ***/
967
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +0200968/* Values for client_data.listen_mode */
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100969#define LISTEN_NONE 0
970#define LISTEN_KERNEL 1
971#define LISTEN_RAW 2
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100972
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +0200973/* Values for client_data.state */
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100974/* initial state: (re)start DHCP negotiation */
975#define INIT_SELECTING 0
976/* discover was sent, DHCPOFFER reply received */
977#define REQUESTING 1
978/* select/renew was sent, DHCPACK reply received */
979#define BOUND 2
980/* half of lease passed, want to renew it by sending unicast renew requests */
981#define RENEWING 3
982/* renew requests were not answered, lease is almost over, send broadcast renew */
983#define REBINDING 4
984/* manually requested renew (SIGUSR1) */
985#define RENEW_REQUESTED 5
986/* release, possibly manually requested (SIGUSR2) */
987#define RELEASED 6
Denys Vlasenko9ba75042011-11-07 15:55:39 +0100988
989static int d6_raw_socket(int ifindex)
990{
991 int fd;
992 struct sockaddr_ll sock;
993
994 /*
995 * Comment:
996 *
997 * I've selected not to see LL header, so BPF doesn't see it, too.
998 * The filter may also pass non-IP and non-ARP packets, but we do
999 * a more complete check when receiving the message in userspace.
1000 *
1001 * and filter shamelessly stolen from:
1002 *
1003 * http://www.flamewarmaster.de/software/dhcpclient/
1004 *
1005 * There are a few other interesting ideas on that page (look under
1006 * "Motivation"). Use of netlink events is most interesting. Think
1007 * of various network servers listening for events and reconfiguring.
1008 * That would obsolete sending HUP signals and/or make use of restarts.
1009 *
1010 * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
1011 * License: GPL v2.
1012 *
1013 * TODO: make conditional?
1014 */
1015#if 0
1016 static const struct sock_filter filter_instr[] = {
1017 /* load 9th byte (protocol) */
1018 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
1019 /* jump to L1 if it is IPPROTO_UDP, else to L4 */
1020 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6),
1021 /* L1: load halfword from offset 6 (flags and frag offset) */
1022 BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6),
1023 /* jump to L4 if any bits in frag offset field are set, else to L2 */
1024 BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0),
1025 /* L2: skip IP header (load index reg with header len) */
1026 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),
1027 /* load udp destination port from halfword[header_len + 2] */
1028 BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2),
Denys Vlasenko04ac6e02013-01-28 15:25:35 +01001029 /* jump to L3 if udp dport is CLIENT_PORT6, else to L4 */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001030 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1),
1031 /* L3: accept packet */
Denys Vlasenkoffc3a932014-02-19 14:17:11 +01001032 BPF_STMT(BPF_RET|BPF_K, 0x7fffffff),
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001033 /* L4: discard packet */
1034 BPF_STMT(BPF_RET|BPF_K, 0),
1035 };
1036 static const struct sock_fprog filter_prog = {
1037 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
1038 /* casting const away: */
1039 .filter = (struct sock_filter *) filter_instr,
1040 };
1041#endif
1042
Denys Vlasenko168f0ef2017-07-21 12:04:22 +02001043 log2("opening raw socket on ifindex %d", ifindex);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001044
1045 fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6));
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001046
Denys Vlasenko2b9acc62017-09-29 14:09:02 +02001047 memset(&sock, 0, sizeof(sock)); /* let's be deterministic */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001048 sock.sll_family = AF_PACKET;
1049 sock.sll_protocol = htons(ETH_P_IPV6);
1050 sock.sll_ifindex = ifindex;
Denys Vlasenko2b9acc62017-09-29 14:09:02 +02001051 /*sock.sll_hatype = ARPHRD_???;*/
1052 /*sock.sll_pkttype = PACKET_???;*/
1053 /*sock.sll_halen = ???;*/
1054 /*sock.sll_addr[8] = ???;*/
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001055 xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
1056
1057#if 0
Denys Vlasenko04ac6e02013-01-28 15:25:35 +01001058 if (CLIENT_PORT6 == 546) {
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001059 /* Use only if standard port is in use */
1060 /* Ignoring error (kernel may lack support for this) */
1061 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
1062 sizeof(filter_prog)) >= 0)
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001063 log1("attached filter to raw socket fd %d", fd); // log?
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001064 }
1065#endif
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001066 return fd;
1067}
1068
1069static void change_listen_mode(int new_mode)
1070{
Denys Vlasenko8f2e99c2016-03-30 18:41:23 +02001071 log1("entering listen mode: %s",
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001072 new_mode != LISTEN_NONE
1073 ? (new_mode == LISTEN_KERNEL ? "kernel" : "raw")
1074 : "none"
1075 );
1076
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001077 client_data.listen_mode = new_mode;
1078 if (client_data.sockfd >= 0) {
1079 close(client_data.sockfd);
1080 client_data.sockfd = -1;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001081 }
1082 if (new_mode == LISTEN_KERNEL)
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001083 client_data.sockfd = udhcp_listen_socket(/*INADDR_ANY,*/ CLIENT_PORT6, client_data.interface);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001084 else if (new_mode != LISTEN_NONE)
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001085 client_data.sockfd = d6_raw_socket(client_data.ifindex);
1086 /* else LISTEN_NONE: client_data.sockfd stays closed */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001087}
1088
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001089static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
1090{
Denys Vlasenko0ae53452021-06-02 16:49:20 +02001091 change_listen_mode(LISTEN_NONE);
1092
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001093 /* send release packet */
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001094 if (client_data.state == BOUND
1095 || client_data.state == RENEWING
1096 || client_data.state == REBINDING
1097 || client_data.state == RENEW_REQUESTED
Denys Vlasenko44399e02016-07-03 20:26:44 +02001098 ) {
James Byrne69374872019-07-02 11:35:03 +02001099 bb_simple_info_msg("unicasting a release");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001100 send_d6_release(server_ipv6, our_cur_ipv6); /* unicast */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001101 }
James Byrne69374872019-07-02 11:35:03 +02001102 bb_simple_info_msg("entering released state");
Peter Korsgaardb6355e22016-08-26 18:46:34 +02001103/*
1104 * We can be here on: SIGUSR2,
1105 * or on exit (SIGTERM) and -R "release on quit" is specified.
1106 * Users requested to be notified in all cases, even if not in one
1107 * of the states above.
1108 */
David Decotigny8f48fc02018-05-24 08:30:15 -07001109 d6_run_script_no_option("deconfig");
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001110 client_data.state = RELEASED;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001111}
1112
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001113#if BB_MMU
1114static void client_background(void)
1115{
1116 bb_daemonize(0);
1117 logmode &= ~LOGMODE_STDIO;
1118 /* rewrite pidfile, as our pid is different now */
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001119 write_pidfile(client_data.pidfile);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001120}
1121#endif
1122
1123//usage:#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
1124//usage:# define IF_UDHCP_VERBOSE(...) __VA_ARGS__
1125//usage:#else
1126//usage:# define IF_UDHCP_VERBOSE(...)
1127//usage:#endif
1128//usage:#define udhcpc6_trivial_usage
Denys Vlasenko64211ce2018-01-16 22:23:38 +01001129//usage: "[-fbnq"IF_UDHCP_VERBOSE("v")"odR] [-i IFACE] [-r IPv6] [-s PROG] [-p PIDFILE]\n"
Denys Vlasenko8cc3a742020-12-18 22:51:46 +01001130//usage: " [-x OPT:VAL]... [-O OPT]..." IF_FEATURE_UDHCP_PORT(" [-P PORT]")
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001131//usage:#define udhcpc6_full_usage "\n"
Andre Kalb5f2e00f2020-05-05 18:24:25 +02001132//usage: "\n -i IFACE Interface to use (default "CONFIG_UDHCPC_DEFAULT_INTERFACE")"
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001133//usage: "\n -p FILE Create pidfile"
1134//usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")"
1135//usage: "\n -B Request broadcast replies"
1136//usage: "\n -t N Send up to N discover packets"
1137//usage: "\n -T N Pause between packets (default 3 seconds)"
1138//usage: "\n -A N Wait N seconds (default 20) after failure"
1139//usage: "\n -f Run in foreground"
1140//usage: USE_FOR_MMU(
1141//usage: "\n -b Background if lease is not obtained"
1142//usage: )
1143//usage: "\n -n Exit if lease is not obtained"
1144//usage: "\n -q Exit after obtaining lease"
1145//usage: "\n -R Release IP on exit"
1146//usage: "\n -S Log to syslog too"
1147//usage: IF_FEATURE_UDHCP_PORT(
Denys Vlasenko8cc3a742020-12-18 22:51:46 +01001148//usage: "\n -P PORT Use PORT (default 546)"
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001149//usage: )
1150////usage: IF_FEATURE_UDHCPC_ARPING(
1151////usage: "\n -a Use arping to validate offered address"
1152////usage: )
1153//usage: "\n -O OPT Request option OPT from server (cumulative)"
1154//usage: "\n -o Don't request any options (unless -O is given)"
Denys Vlasenko64211ce2018-01-16 22:23:38 +01001155//usage: "\n -r IPv6 Request this address ('no' to not request any IP)"
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001156//usage: "\n -d Request prefix"
Eivind Versvik22a63392019-08-24 17:23:48 +02001157//usage: "\n -l Send 'information request' instead of 'solicit'"
1158//usage: "\n (used for servers which do not assign IPv6 addresses)"
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001159//usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)"
1160//usage: "\n Examples of string, numeric, and hex byte opts:"
1161//usage: "\n -x hostname:bbox - option 12"
1162//usage: "\n -x lease:3600 - option 51 (lease time)"
1163//usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)"
Denys Vlasenko266f6f12018-04-13 13:18:34 +02001164//usage: "\n -x 14:'\"dumpfile\"' - option 14 (shell-quoted)"
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001165//usage: IF_UDHCP_VERBOSE(
1166//usage: "\n -v Verbose"
1167//usage: )
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001168//usage: "\nSignals:"
1169//usage: "\n USR1 Renew lease"
1170//usage: "\n USR2 Release lease"
1171
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001172int udhcpc6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1173int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1174{
1175 const char *str_r;
1176 IF_FEATURE_UDHCP_PORT(char *str_P;)
Denys Vlasenko265fcdd2021-06-02 13:50:26 +02001177 uint8_t *clientid_mac_ptr;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001178 llist_t *list_O = NULL;
1179 llist_t *list_x = NULL;
1180 int tryagain_timeout = 20;
1181 int discover_timeout = 3;
1182 int discover_retries = 3;
1183 struct in6_addr srv6_buf;
1184 struct in6_addr ipv6_buf;
1185 struct in6_addr *requested_ipv6;
1186 uint32_t xid = 0;
1187 int packet_num;
1188 int timeout; /* must be signed */
Denys Vlasenkoa1a77ad2021-05-01 11:54:08 +02001189 int lease_remaining; /* must be signed */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001190 unsigned opt;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001191 int retval;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001192
Denys Vlasenkodf70a432016-04-21 18:54:36 +02001193 setup_common_bufsiz();
Denys Vlasenkof6dd9e02018-01-19 18:44:19 +01001194 /* We want random_xid to be random */
1195 srand(monotonic_us());
Denys Vlasenkodf70a432016-04-21 18:54:36 +02001196
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001197 /* Default options */
Denys Vlasenko04ac6e02013-01-28 15:25:35 +01001198 IF_FEATURE_UDHCP_PORT(SERVER_PORT6 = 547;)
1199 IF_FEATURE_UDHCP_PORT(CLIENT_PORT6 = 546;)
Andre Kalb5f2e00f2020-05-05 18:24:25 +02001200 client_data.interface = CONFIG_UDHCPC_DEFAULT_INTERFACE;
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001201 client_data.script = CONFIG_UDHCPC_DEFAULT_SCRIPT;
1202 client_data.sockfd = -1;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001203
Denys Vlasenko65c34c52019-05-31 23:39:22 +02001204 /* Make sure fd 0,1,2 are open */
1205 /* Set up the signal pipe on fds 3,4 - must be before openlog() */
1206 udhcp_sp_setup();
1207
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001208 /* Parse command line */
Denys Vlasenko22542ec2017-08-08 21:55:02 +02001209 opt = getopt32long(argv, "^"
1210 /* O,x: list; -T,-t,-A take numeric param */
Eivind Versvik22a63392019-08-24 17:23:48 +02001211 "i:np:qRr:s:T:+t:+SA:+O:*ox:*fld"
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001212 USE_FOR_MMU("b")
1213 ///IF_FEATURE_UDHCPC_ARPING("a")
1214 IF_FEATURE_UDHCP_PORT("P:")
1215 "v"
Denys Vlasenko22542ec2017-08-08 21:55:02 +02001216 "\0" IF_UDHCP_VERBOSE("vv") /* -v is a counter */
Denys Vlasenko036585a2017-08-08 16:38:18 +02001217 , udhcpc6_longopts
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001218 , &client_data.interface, &client_data.pidfile, &str_r /* i,p */
1219 , &client_data.script /* s */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001220 , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */
1221 , &list_O
1222 , &list_x
1223 IF_FEATURE_UDHCP_PORT(, &str_P)
1224 IF_UDHCP_VERBOSE(, &dhcp_verbose)
Denys Vlasenko7e21f042011-11-08 11:39:41 +01001225 );
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001226 requested_ipv6 = NULL;
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001227 option_mask32 |= OPT_r;
Eivind Versvik22a63392019-08-24 17:23:48 +02001228 if (opt & OPT_l) {
1229 /* for -l, do not require IPv6 assignment from server */
1230 option_mask32 &= ~OPT_r;
1231 } else if (opt & OPT_r) {
1232 /* explicit "-r ARG" given */
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001233 if (strcmp(str_r, "no") == 0) {
Eivind Versvik22a63392019-08-24 17:23:48 +02001234 option_mask32 &= ~OPT_r;
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001235 } else {
1236 if (inet_pton(AF_INET6, str_r, &ipv6_buf) <= 0)
1237 bb_error_msg_and_die("bad IPv6 address '%s'", str_r);
1238 requested_ipv6 = &ipv6_buf;
1239 }
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001240 }
Eivind Versvik22a63392019-08-24 17:23:48 +02001241
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001242#if ENABLE_FEATURE_UDHCP_PORT
1243 if (opt & OPT_P) {
Denys Vlasenko04ac6e02013-01-28 15:25:35 +01001244 CLIENT_PORT6 = xatou16(str_P);
1245 SERVER_PORT6 = CLIENT_PORT6 + 1;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001246 }
1247#endif
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001248 while (list_O) {
1249 char *optstr = llist_pop(&list_O);
1250 unsigned n = bb_strtou(optstr, NULL, 0);
1251 if (errno || n > 254) {
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +02001252 n = udhcp_option_idx(optstr, d6_option_strings);
1253 n = d6_optflags[n].code;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001254 }
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001255 client_data.opt_mask[n >> 3] |= 1 << (n & 7);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001256 }
Denys Vlasenko293c9452012-07-27 13:25:07 +02001257 if (!(opt & OPT_o)) {
Denys Vlasenko293c9452012-07-27 13:25:07 +02001258 unsigned i, n;
Denys Vlasenkoba4fbca2017-06-28 19:18:17 +02001259 for (i = 0; (n = d6_optflags[i].code) != 0; i++) {
1260 if (d6_optflags[i].flags & OPTION_REQ) {
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001261 client_data.opt_mask[n >> 3] |= 1 << (n & 7);
Denys Vlasenko293c9452012-07-27 13:25:07 +02001262 }
1263 }
Denys Vlasenko293c9452012-07-27 13:25:07 +02001264 }
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001265 while (list_x) {
Denys Vlasenko266f6f12018-04-13 13:18:34 +02001266 char *optstr = xstrdup(llist_pop(&list_x));
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001267 udhcp_str2optset(optstr, &client_data.options,
Denys Vlasenko60275972018-05-14 11:06:35 +02001268 d6_optflags, d6_option_strings,
1269 /*dhcpv6:*/ 1
1270 );
Denys Vlasenko266f6f12018-04-13 13:18:34 +02001271 free(optstr);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001272 }
1273
Denys Vlasenko265fcdd2021-06-02 13:50:26 +02001274 clientid_mac_ptr = NULL;
1275 if (!udhcp_find_option(client_data.options, D6_OPT_CLIENTID)) {
1276 /* not set, set the default client ID */
Denys Vlasenko698cdef2021-06-02 15:07:46 +02001277 clientid_mac_ptr = udhcp_insert_new_option(
1278 &client_data.options, D6_OPT_CLIENTID,
Denys Vlasenko949e9622021-06-02 15:51:50 +02001279 2+2 + 6, /*dhcp6:*/ 1);
1280 clientid_mac_ptr += 2+2; /* skip option code, len */
1281 clientid_mac_ptr[1] = 3; /* DUID-LL */
1282 clientid_mac_ptr[3] = 1; /* type: ethernet */
1283 clientid_mac_ptr += 2+2; /* skip DUID-LL, ethernet */
1284 }
1285
1286 if (d6_read_interface(client_data.interface,
1287 &client_data.ifindex,
1288 &client6_data.ll_ip6,
1289 client_data.client_mac)
1290 ) {
1291 return 1;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001292 }
1293
1294#if !BB_MMU
1295 /* on NOMMU reexec (i.e., background) early */
1296 if (!(opt & OPT_f)) {
1297 bb_daemonize_or_rexec(0 /* flags */, argv);
1298 logmode = LOGMODE_NONE;
1299 }
1300#endif
1301 if (opt & OPT_S) {
1302 openlog(applet_name, LOG_PID, LOG_DAEMON);
1303 logmode |= LOGMODE_SYSLOG;
1304 }
1305
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001306 /* Create pidfile */
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001307 write_pidfile(client_data.pidfile);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001308 /* Goes to stdout (unless NOMMU) and possibly syslog */
James Byrne69374872019-07-02 11:35:03 +02001309 bb_simple_info_msg("started, v"BB_VER);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001310
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001311 client_data.state = INIT_SELECTING;
David Decotigny8f48fc02018-05-24 08:30:15 -07001312 d6_run_script_no_option("deconfig");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001313 packet_num = 0;
1314 timeout = 0;
Denys Vlasenkoa1a77ad2021-05-01 11:54:08 +02001315 lease_remaining = 0;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001316
1317 /* Main event loop. select() waits on signal pipe and possibly
1318 * on sockfd.
1319 * "continue" statements in code below jump to the top of the loop.
1320 */
1321 for (;;) {
Denys Vlasenko52a515d2017-02-16 23:25:44 +01001322 struct pollfd pfds[2];
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001323 struct d6_packet packet;
1324 uint8_t *packet_end;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001325
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001326 //bb_error_msg("sockfd:%d, listen_mode:%d", client_data.sockfd, client_data.listen_mode);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001327
1328 /* Was opening raw or udp socket here
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001329 * if (client_data.listen_mode != LISTEN_NONE && client_data.sockfd < 0),
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001330 * but on fast network renew responses return faster
1331 * than we open sockets. Thus this code is moved
1332 * to change_listen_mode(). Thus we open listen socket
1333 * BEFORE we send renew request (see "case BOUND:"). */
1334
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001335 udhcp_sp_fd_set(pfds, client_data.sockfd);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001336
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001337 retval = 0;
1338 /* If we already timed out, fall through with retval = 0, else... */
Denys Vlasenkoa1a77ad2021-05-01 11:54:08 +02001339 if (timeout > 0) {
1340 unsigned diff;
1341
1342 if (timeout > INT_MAX/1000)
1343 timeout = INT_MAX/1000;
1344 log1("waiting %u seconds", timeout);
1345 diff = (unsigned)monotonic_sec();
1346 retval = poll(pfds, 2, timeout * 1000);
Denys Vlasenko687f41f2021-06-01 00:19:03 +02001347 diff = (unsigned)monotonic_sec() - diff;
1348 lease_remaining -= diff;
1349 if (lease_remaining < 0)
1350 lease_remaining = 0;
1351 timeout -= diff;
1352 if (timeout < 0)
1353 timeout = 0;
1354
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001355 if (retval < 0) {
1356 /* EINTR? A signal was caught, don't panic */
1357 if (errno == EINTR) {
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001358 continue;
1359 }
1360 /* Else: an error occured, panic! */
James Byrne69374872019-07-02 11:35:03 +02001361 bb_simple_perror_msg_and_die("poll");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001362 }
1363 }
1364
1365 /* If timeout dropped to zero, time to become active:
1366 * resend discover/renew/whatever
1367 */
1368 if (retval == 0) {
1369 /* When running on a bridge, the ifindex may have changed
1370 * (e.g. if member interfaces were added/removed
1371 * or if the status of the bridge changed).
1372 * Refresh ifindex and client_mac:
1373 */
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001374 if (d6_read_interface(client_data.interface,
1375 &client_data.ifindex,
Denys Vlasenkoe09f5e32017-03-27 22:10:15 +02001376 &client6_data.ll_ip6,
Denys Vlasenko949e9622021-06-02 15:51:50 +02001377 client_data.client_mac)
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001378 ) {
1379 goto ret0; /* iface is gone? */
1380 }
Denys Vlasenkoe09f5e32017-03-27 22:10:15 +02001381
Denys Vlasenko265fcdd2021-06-02 13:50:26 +02001382 if (clientid_mac_ptr)
Denys Vlasenko949e9622021-06-02 15:51:50 +02001383 memcpy(clientid_mac_ptr, client_data.client_mac, 6);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001384
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001385 switch (client_data.state) {
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001386 case INIT_SELECTING:
Felix Fietkau1c7a58d2012-09-27 16:22:24 +02001387 if (!discover_retries || packet_num < discover_retries) {
Denys Vlasenko0ae53452021-06-02 16:49:20 +02001388 if (packet_num == 0) {
1389 change_listen_mode(LISTEN_RAW);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001390 xid = random_xid();
Denys Vlasenko0ae53452021-06-02 16:49:20 +02001391 }
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001392 /* multicast */
Eivind Versvik22a63392019-08-24 17:23:48 +02001393 if (opt & OPT_l)
1394 send_d6_info_request(xid);
1395 else
1396 send_d6_discover(xid, requested_ipv6);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001397 timeout = discover_timeout;
1398 packet_num++;
1399 continue;
1400 }
1401 leasefail:
Denys Vlasenko0ae53452021-06-02 16:49:20 +02001402 change_listen_mode(LISTEN_NONE);
David Decotigny8f48fc02018-05-24 08:30:15 -07001403 d6_run_script_no_option("leasefail");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001404#if BB_MMU /* -b is not supported on NOMMU */
1405 if (opt & OPT_b) { /* background if no lease */
James Byrne69374872019-07-02 11:35:03 +02001406 bb_simple_info_msg("no lease, forking to background");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001407 client_background();
1408 /* do not background again! */
Andrey Mazo87e21622019-02-22 16:46:19 -05001409 opt = ((opt & ~(OPT_b|OPT_n)) | OPT_f);
1410 /* ^^^ also disables -n (-b takes priority over -n):
1411 * ifup's default udhcpc options are -R -n,
1412 * and users want to be able to add -b
1413 * (in a config file) to make it background
1414 * _and not exit_.
1415 */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001416 } else
1417#endif
1418 if (opt & OPT_n) { /* abort if no lease */
James Byrne69374872019-07-02 11:35:03 +02001419 bb_simple_info_msg("no lease, failing");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001420 retval = 1;
1421 goto ret;
1422 }
Denys Vlasenko1c725372021-06-03 09:20:45 +02001423 /* Wait before trying again */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001424 timeout = tryagain_timeout;
1425 packet_num = 0;
1426 continue;
1427 case REQUESTING:
Felix Fietkau1c7a58d2012-09-27 16:22:24 +02001428 if (!discover_retries || packet_num < discover_retries) {
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001429 /* send multicast select packet */
1430 send_d6_select(xid);
1431 timeout = discover_timeout;
1432 packet_num++;
1433 continue;
1434 }
1435 /* Timed out, go back to init state.
1436 * "discover...select...discover..." loops
1437 * were seen in the wild. Treat them similarly
1438 * to "no response to discover" case */
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001439 client_data.state = INIT_SELECTING;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001440 goto leasefail;
1441 case BOUND:
1442 /* 1/2 lease passed, enter renewing state */
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001443 client_data.state = RENEWING;
1444 client_data.first_secs = 0; /* make secs field count from 0 */
Denys Vlasenko1c725372021-06-03 09:20:45 +02001445 got_SIGUSR1:
James Byrne69374872019-07-02 11:35:03 +02001446 log1s("entering renew state");
Denys Vlasenko1c725372021-06-03 09:20:45 +02001447 change_listen_mode(LISTEN_KERNEL);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001448 /* fall right through */
Denys Vlasenko1c725372021-06-03 09:20:45 +02001449 case RENEW_REQUESTED: /* in manual (SIGUSR1) renew */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001450 case RENEWING:
Denys Vlasenko1c725372021-06-03 09:20:45 +02001451 if (packet_num == 0) {
1452 /* Send an unicast renew request */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001453 /* Sometimes observed to fail (EADDRNOTAVAIL) to bind
1454 * a new UDP socket for sending inside send_renew.
1455 * I hazard to guess existing listening socket
1456 * is somehow conflicting with it, but why is it
1457 * not deterministic then?! Strange.
1458 * Anyway, it does recover by eventually failing through
1459 * into INIT_SELECTING state.
1460 */
Eivind Versvik22a63392019-08-24 17:23:48 +02001461 if (opt & OPT_l)
1462 send_d6_info_request(xid);
1463 else
1464 send_d6_renew(xid, &srv6_buf, requested_ipv6);
Denys Vlasenkoa1a77ad2021-05-01 11:54:08 +02001465 timeout = discover_timeout;
Denys Vlasenko687f41f2021-06-01 00:19:03 +02001466 packet_num++;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001467 continue;
Denys Vlasenko1c725372021-06-03 09:20:45 +02001468 } /* else: we had sent one packet, but got no reply */
1469 log1s("no response to renew");
1470 if (lease_remaining > 30) {
1471 /* Some lease time remains, try to renew later */
1472 change_listen_mode(LISTEN_NONE);
1473 goto BOUND_for_half_lease;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001474 }
Denys Vlasenko1c725372021-06-03 09:20:45 +02001475 /* Enter rebinding state */
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001476 client_data.state = REBINDING;
Denys Vlasenko1c725372021-06-03 09:20:45 +02001477 log1s("entering rebinding state");
Denys Vlasenko0ae53452021-06-02 16:49:20 +02001478 /* Switch to bcast receive */
1479 change_listen_mode(LISTEN_RAW);
Denys Vlasenko687f41f2021-06-01 00:19:03 +02001480 packet_num = 0;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001481 /* fall right through */
1482 case REBINDING:
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001483 /* Lease is *really* about to run out,
1484 * try to find DHCP server using broadcast */
Denys Vlasenko687f41f2021-06-01 00:19:03 +02001485 if (lease_remaining > 0 && packet_num < 3) {
Eivind Versvik22a63392019-08-24 17:23:48 +02001486 if (opt & OPT_l)
1487 send_d6_info_request(xid);
1488 else /* send a broadcast renew request */
1489 send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6);
Denys Vlasenkoa1a77ad2021-05-01 11:54:08 +02001490 timeout = discover_timeout;
Denys Vlasenko687f41f2021-06-01 00:19:03 +02001491 packet_num++;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001492 continue;
1493 }
1494 /* Timed out, enter init state */
Denys Vlasenko0ae53452021-06-02 16:49:20 +02001495 change_listen_mode(LISTEN_NONE);
James Byrne69374872019-07-02 11:35:03 +02001496 bb_simple_info_msg("lease lost, entering init state");
David Decotigny8f48fc02018-05-24 08:30:15 -07001497 d6_run_script_no_option("deconfig");
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001498 client_data.state = INIT_SELECTING;
1499 client_data.first_secs = 0; /* make secs field count from 0 */
Denys Vlasenkoa1a77ad2021-05-01 11:54:08 +02001500 timeout = 0;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001501 packet_num = 0;
1502 continue;
1503 /* case RELEASED: */
1504 }
Denys Vlasenko265fcdd2021-06-02 13:50:26 +02001505 /* RELEASED state (when we got SIGUSR2) ends up here.
1506 * (wait for SIGUSR1 to re-init, or for TERM, etc)
1507 */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001508 timeout = INT_MAX;
1509 continue; /* back to main loop */
Denys Vlasenko3293bc12018-03-10 19:01:48 +01001510 } /* if poll timed out */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001511
Denys Vlasenko3293bc12018-03-10 19:01:48 +01001512 /* poll() didn't timeout, something happened */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001513
1514 /* Is it a signal? */
Denys Vlasenko3293bc12018-03-10 19:01:48 +01001515 switch (udhcp_sp_read()) {
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001516 case SIGUSR1:
Denys Vlasenko1c725372021-06-03 09:20:45 +02001517 if (client_data.state <= REQUESTING)
1518 /* Initial negotiations in progress, do not disturb */
1519 break;
Denys Vlasenkoecaf8e82021-06-03 16:22:35 +02001520 if (client_data.state == REBINDING)
1521 /* Do not go back from rebind to renew state */
1522 break;
Denys Vlasenko1c725372021-06-03 09:20:45 +02001523
1524 if (lease_remaining > 30) /* if renew fails, do not go back to BOUND */
1525 lease_remaining = 30;
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001526 client_data.first_secs = 0; /* make secs field count from 0 */
Denys Vlasenko1c725372021-06-03 09:20:45 +02001527 packet_num = 0;
Denys Vlasenkoe6007c42021-05-01 13:48:50 +02001528
1529 switch (client_data.state) {
Denys Vlasenkoe6007c42021-05-01 13:48:50 +02001530 case BOUND:
1531 case RENEWING:
Denys Vlasenkoecaf8e82021-06-03 16:22:35 +02001532 /* Try to renew/rebind */
Denys Vlasenkoe6007c42021-05-01 13:48:50 +02001533 change_listen_mode(LISTEN_KERNEL);
1534 client_data.state = RENEW_REQUESTED;
Denys Vlasenko1c725372021-06-03 09:20:45 +02001535 goto got_SIGUSR1;
Denys Vlasenkoe6007c42021-05-01 13:48:50 +02001536
Denys Vlasenko1c725372021-06-03 09:20:45 +02001537 case RENEW_REQUESTED:
Denys Vlasenkoecaf8e82021-06-03 16:22:35 +02001538 /* Two SIGUSR1 received, start things over */
Denys Vlasenko0ae53452021-06-02 16:49:20 +02001539 change_listen_mode(LISTEN_NONE);
Denys Vlasenkoe6007c42021-05-01 13:48:50 +02001540 d6_run_script_no_option("deconfig");
Denys Vlasenko0ae53452021-06-02 16:49:20 +02001541
1542 default:
Denys Vlasenko0ae53452021-06-02 16:49:20 +02001543 /* case RELEASED: */
Denys Vlasenkoecaf8e82021-06-03 16:22:35 +02001544 /* Wake from SIGUSR2-induced deconfigured state */
Denys Vlasenko0ae53452021-06-02 16:49:20 +02001545 change_listen_mode(LISTEN_NONE);
Denys Vlasenkoe6007c42021-05-01 13:48:50 +02001546 }
Denys Vlasenkoe6007c42021-05-01 13:48:50 +02001547 client_data.state = INIT_SELECTING;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001548 /* Kill any timeouts, user wants this to hurry along */
1549 timeout = 0;
1550 continue;
1551 case SIGUSR2:
1552 perform_d6_release(&srv6_buf, requested_ipv6);
Denys Vlasenko0ae53452021-06-02 16:49:20 +02001553 /* ^^^ switches to LISTEN_NONE */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001554 timeout = INT_MAX;
1555 continue;
1556 case SIGTERM:
James Byrne253c4e72019-04-12 17:01:51 +00001557 bb_info_msg("received %s", "SIGTERM");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001558 goto ret0;
1559 }
1560
1561 /* Is it a packet? */
Denys Vlasenko3293bc12018-03-10 19:01:48 +01001562 if (!pfds[1].revents)
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001563 continue; /* no */
1564
1565 {
1566 int len;
1567
1568 /* A packet is ready, read it */
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001569 if (client_data.listen_mode == LISTEN_KERNEL)
1570 len = d6_recv_kernel_packet(&srv6_buf, &packet, client_data.sockfd);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001571 else
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001572 len = d6_recv_raw_packet(&srv6_buf, &packet, client_data.sockfd);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001573 if (len == -1) {
1574 /* Error is severe, reopen socket */
Denys Vlasenko6f97b302017-09-29 18:17:25 +02001575 bb_error_msg("read error: "STRERROR_FMT", reopening socket" STRERROR_ERRNO);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001576 sleep(discover_timeout); /* 3 seconds by default */
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001577 change_listen_mode(client_data.listen_mode); /* just close and reopen */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001578 }
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001579 if (len < 0)
1580 continue;
1581 packet_end = (uint8_t*)&packet + len;
1582 }
1583
1584 if ((packet.d6_xid32 & htonl(0x00ffffff)) != xid) {
Denys Vlasenkoa4959ee2021-02-21 16:32:07 +01001585 log1("xid %x (our is %x)%s",
1586 (unsigned)(packet.d6_xid32 & htonl(0x00ffffff)), (unsigned)xid,
1587 ", ignoring packet"
1588 );
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001589 continue;
1590 }
1591
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001592 switch (client_data.state) {
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001593 case INIT_SELECTING:
1594 if (packet.d6_msg_type == D6_MSG_ADVERTISE)
1595 goto type_is_ok;
1596 /* DHCPv6 has "Rapid Commit", when instead of Advertise,
1597 * server sends Reply right away.
1598 * Fall through to check for this case.
1599 */
1600 case REQUESTING:
1601 case RENEWING:
1602 case RENEW_REQUESTED:
1603 case REBINDING:
1604 if (packet.d6_msg_type == D6_MSG_REPLY) {
Denys Vlasenkoa1a77ad2021-05-01 11:54:08 +02001605 unsigned start;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001606 uint32_t lease_seconds;
Denys Vlasenko14f13202018-01-16 21:47:10 +01001607 struct d6_option *option;
Denys Vlasenko7c44b602018-01-17 13:55:51 +01001608 unsigned address_timeout;
1609 unsigned prefix_timeout;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001610 type_is_ok:
Denys Vlasenko0ae53452021-06-02 16:49:20 +02001611 change_listen_mode(LISTEN_NONE);
1612
Denys Vlasenko14f13202018-01-16 21:47:10 +01001613 address_timeout = 0;
1614 prefix_timeout = 0;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001615 option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
Denys Vlasenko64d58aa2017-03-27 22:22:09 +02001616 if (option && (option->data[0] | option->data[1]) != 0) {
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001617 /* return to init state */
James Byrne253c4e72019-04-12 17:01:51 +00001618 bb_info_msg("received DHCP NAK (%u)", option->data[4]);
David Decotigny8f48fc02018-05-24 08:30:15 -07001619 d6_run_script(packet.d6_options,
1620 packet_end, "nak");
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001621 if (client_data.state != REQUESTING)
David Decotigny8f48fc02018-05-24 08:30:15 -07001622 d6_run_script_no_option("deconfig");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001623 sleep(3); /* avoid excessive network traffic */
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001624 client_data.state = INIT_SELECTING;
1625 client_data.first_secs = 0; /* make secs field count from 0 */
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001626 requested_ipv6 = NULL;
1627 timeout = 0;
1628 packet_num = 0;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001629 continue;
1630 }
1631 option = d6_copy_option(packet.d6_options, packet_end, D6_OPT_SERVERID);
1632 if (!option) {
James Byrne69374872019-07-02 11:35:03 +02001633 bb_simple_info_msg("no server ID, ignoring packet");
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001634 continue;
1635 /* still selecting - this server looks bad */
1636 }
1637//Note: we do not bother comparing server IDs in Advertise and Reply msgs.
1638//server_id variable is used solely for creation of proper server_id option
1639//in outgoing packets. (why DHCPv6 even introduced it is a mystery).
1640 free(client6_data.server_id);
1641 client6_data.server_id = option;
1642 if (packet.d6_msg_type == D6_MSG_ADVERTISE) {
1643 /* enter requesting state */
Denys Vlasenko0ae53452021-06-02 16:49:20 +02001644 change_listen_mode(LISTEN_RAW);
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001645 client_data.state = REQUESTING;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001646 timeout = 0;
1647 packet_num = 0;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001648 continue;
1649 }
1650 /* It's a D6_MSG_REPLY */
1651/*
1652 * RFC 3315 18.1.8. Receipt of Reply Messages
1653 *
1654 * Upon the receipt of a valid Reply message in response to a Solicit
1655 * (with a Rapid Commit option), Request, Confirm, Renew, Rebind or
1656 * Information-request message, the client extracts the configuration
1657 * information contained in the Reply. The client MAY choose to report
1658 * any status code or message from the status code option in the Reply
1659 * message.
1660 *
1661 * The client SHOULD perform duplicate address detection [17] on each of
1662 * the addresses in any IAs it receives in the Reply message before
1663 * using that address for traffic. If any of the addresses are found to
1664 * be in use on the link, the client sends a Decline message to the
1665 * server as described in section 18.1.7.
1666 *
1667 * If the Reply was received in response to a Solicit (with a Rapid
1668 * Commit option), Request, Renew or Rebind message, the client updates
1669 * the information it has recorded about IAs from the IA options
1670 * contained in the Reply message:
1671 *
1672 * - Record T1 and T2 times.
1673 *
1674 * - Add any new addresses in the IA option to the IA as recorded by
1675 * the client.
1676 *
1677 * - Update lifetimes for any addresses in the IA option that the
1678 * client already has recorded in the IA.
1679 *
1680 * - Discard any addresses from the IA, as recorded by the client, that
1681 * have a valid lifetime of 0 in the IA Address option.
1682 *
1683 * - Leave unchanged any information about addresses the client has
1684 * recorded in the IA but that were not included in the IA from the
1685 * server.
1686 *
1687 * Management of the specific configuration information is detailed in
1688 * the definition of each option in section 22.
1689 *
1690 * If the client receives a Reply message with a Status Code containing
1691 * UnspecFail, the server is indicating that it was unable to process
1692 * the message due to an unspecified failure condition. If the client
1693 * retransmits the original message to the same server to retry the
1694 * desired operation, the client MUST limit the rate at which it
1695 * retransmits the message and limit the duration of the time during
1696 * which it retransmits the message.
1697 *
1698 * When the client receives a Reply message with a Status Code option
1699 * with the value UseMulticast, the client records the receipt of the
1700 * message and sends subsequent messages to the server through the
1701 * interface on which the message was received using multicast. The
1702 * client resends the original message using multicast.
1703 *
1704 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1705 * | OPTION_IA_NA | option-len |
1706 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1707 * | IAID (4 octets) |
1708 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1709 * | T1 |
1710 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1711 * | T2 |
1712 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1713 * | |
1714 * . IA_NA-options .
1715 * . .
1716 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1717 *
1718 *
1719 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1720 * | OPTION_IAADDR | option-len |
1721 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1722 * | |
1723 * | IPv6 address |
1724 * | |
1725 * | |
1726 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1727 * | preferred-lifetime |
1728 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1729 * | valid-lifetime |
1730 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1731 * . .
1732 * . IAaddr-options .
1733 * . .
1734 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1735 */
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001736 if (option_mask32 & OPT_r) {
Denys Vlasenko14f13202018-01-16 21:47:10 +01001737 struct d6_option *iaaddr;
1738
Denys Vlasenko1e8d79b2018-01-16 21:35:39 +01001739 free(client6_data.ia_na);
1740 client6_data.ia_na = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_NA);
1741 if (!client6_data.ia_na) {
Denys Vlasenkoa4959ee2021-02-21 16:32:07 +01001742 bb_info_msg("no %s option%s", "IA_NA", ", ignoring packet");
Denys Vlasenko1e8d79b2018-01-16 21:35:39 +01001743 continue;
1744 }
1745 if (client6_data.ia_na->len < (4 + 4 + 4) + (2 + 2 + 16 + 4 + 4)) {
James Byrne253c4e72019-04-12 17:01:51 +00001746 bb_info_msg("%s option is too short:%d bytes",
Denys Vlasenko14f13202018-01-16 21:47:10 +01001747 "IA_NA", client6_data.ia_na->len);
Denys Vlasenko1e8d79b2018-01-16 21:35:39 +01001748 continue;
1749 }
1750 iaaddr = d6_find_option(client6_data.ia_na->data + 4 + 4 + 4,
1751 client6_data.ia_na->data + client6_data.ia_na->len,
1752 D6_OPT_IAADDR
1753 );
1754 if (!iaaddr) {
Denys Vlasenkoa4959ee2021-02-21 16:32:07 +01001755 bb_info_msg("no %s option%s", "IAADDR", ", ignoring packet");
Denys Vlasenko1e8d79b2018-01-16 21:35:39 +01001756 continue;
1757 }
1758 if (iaaddr->len < (16 + 4 + 4)) {
James Byrne253c4e72019-04-12 17:01:51 +00001759 bb_info_msg("%s option is too short:%d bytes",
Denys Vlasenko14f13202018-01-16 21:47:10 +01001760 "IAADDR", iaaddr->len);
Denys Vlasenko1e8d79b2018-01-16 21:35:39 +01001761 continue;
1762 }
1763 /* Note: the address is sufficiently aligned for cast:
1764 * we _copied_ IA-NA, and copy is always well-aligned.
1765 */
1766 requested_ipv6 = (struct in6_addr*) iaaddr->data;
1767 move_from_unaligned32(lease_seconds, iaaddr->data + 16 + 4);
1768 lease_seconds = ntohl(lease_seconds);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001769/// TODO: check for 0 lease time?
James Byrne253c4e72019-04-12 17:01:51 +00001770 bb_info_msg("%s obtained, lease time %u",
Denys Vlasenko14f13202018-01-16 21:47:10 +01001771 "IPv6", /*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds);
Denys Vlasenko7c44b602018-01-17 13:55:51 +01001772 address_timeout = lease_seconds;
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001773 }
1774 if (option_mask32 & OPT_d) {
1775 struct d6_option *iaprefix;
1776
1777 free(client6_data.ia_pd);
1778 client6_data.ia_pd = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_PD);
1779 if (!client6_data.ia_pd) {
Denys Vlasenkoa4959ee2021-02-21 16:32:07 +01001780 bb_info_msg("no %s option%s", "IA_PD", ", ignoring packet");
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001781 continue;
1782 }
1783 if (client6_data.ia_pd->len < (4 + 4 + 4) + (2 + 2 + 4 + 4 + 1 + 16)) {
James Byrne253c4e72019-04-12 17:01:51 +00001784 bb_info_msg("%s option is too short:%d bytes",
Denys Vlasenko14f13202018-01-16 21:47:10 +01001785 "IA_PD", client6_data.ia_pd->len);
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001786 continue;
1787 }
1788 iaprefix = d6_find_option(client6_data.ia_pd->data + 4 + 4 + 4,
1789 client6_data.ia_pd->data + client6_data.ia_pd->len,
1790 D6_OPT_IAPREFIX
1791 );
1792 if (!iaprefix) {
Denys Vlasenkoa4959ee2021-02-21 16:32:07 +01001793 bb_info_msg("no %s option%s", "IAPREFIX", ", ignoring packet");
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001794 continue;
1795 }
1796 if (iaprefix->len < (4 + 4 + 1 + 16)) {
James Byrne253c4e72019-04-12 17:01:51 +00001797 bb_info_msg("%s option is too short:%d bytes",
Denys Vlasenko14f13202018-01-16 21:47:10 +01001798 "IAPREFIX", iaprefix->len);
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001799 continue;
1800 }
1801 move_from_unaligned32(lease_seconds, iaprefix->data + 4);
1802 lease_seconds = ntohl(lease_seconds);
James Byrne253c4e72019-04-12 17:01:51 +00001803 bb_info_msg("%s obtained, lease time %u",
Denys Vlasenko14f13202018-01-16 21:47:10 +01001804 "prefix", /*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds);
Denys Vlasenko7c44b602018-01-17 13:55:51 +01001805 prefix_timeout = lease_seconds;
Denys Vlasenkoef5207f2018-01-16 21:39:14 +01001806 }
Denys Vlasenko6e9e6d82018-01-16 21:52:23 +01001807 if (!address_timeout)
1808 address_timeout = prefix_timeout;
1809 if (!prefix_timeout)
1810 prefix_timeout = address_timeout;
Denys Vlasenkoa1a77ad2021-05-01 11:54:08 +02001811 lease_remaining = (prefix_timeout < address_timeout ? prefix_timeout : address_timeout);
1812 if (lease_remaining < 0) /* signed overflow? */
1813 lease_remaining = INT_MAX;
Eivind Versvik22a63392019-08-24 17:23:48 +02001814 if (opt & OPT_l) {
1815 /* TODO: request OPTION_INFORMATION_REFRESH_TIME (32)
1816 * and use its value instead of the default 1 day.
1817 */
Denys Vlasenkoa1a77ad2021-05-01 11:54:08 +02001818 lease_remaining = 24 * 60 * 60;
Eivind Versvik22a63392019-08-24 17:23:48 +02001819 }
Denys Vlasenko14f13202018-01-16 21:47:10 +01001820 /* paranoia: must not be too small */
Denys Vlasenkoa1a77ad2021-05-01 11:54:08 +02001821 if (lease_remaining < 30)
1822 lease_remaining = 30;
1823
Denys Vlasenko14f13202018-01-16 21:47:10 +01001824 /* enter bound state */
Denys Vlasenkoa1a77ad2021-05-01 11:54:08 +02001825 start = monotonic_sec();
David Decotigny8f48fc02018-05-24 08:30:15 -07001826 d6_run_script(packet.d6_options, packet_end,
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001827 (client_data.state == REQUESTING ? "bound" : "renew"));
Denys Vlasenko687f41f2021-06-01 00:19:03 +02001828 lease_remaining -= (unsigned)monotonic_sec() - start;
1829 if (lease_remaining < 0)
1830 lease_remaining = 0;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001831 if (opt & OPT_q) { /* quit after lease */
1832 goto ret0;
1833 }
1834 /* future renew failures should not exit (JM) */
1835 opt &= ~OPT_n;
1836#if BB_MMU /* NOMMU case backgrounded earlier */
1837 if (!(opt & OPT_f)) {
1838 client_background();
1839 /* do not background again! */
1840 opt = ((opt & ~OPT_b) | OPT_f);
1841 }
1842#endif
Denys Vlasenko687f41f2021-06-01 00:19:03 +02001843
Denys Vlasenko1c725372021-06-03 09:20:45 +02001844 BOUND_for_half_lease:
Denys Vlasenko687f41f2021-06-01 00:19:03 +02001845 timeout = (unsigned)lease_remaining / 2;
1846 client_data.state = BOUND;
Denys Vlasenko687f41f2021-06-01 00:19:03 +02001847 packet_num = 0;
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001848 continue; /* back to main loop */
1849 }
1850 continue;
1851 /* case BOUND: - ignore all packets */
1852 /* case RELEASED: - ignore all packets */
1853 }
1854 /* back to main loop */
1855 } /* for (;;) - main loop ends */
1856
1857 ret0:
1858 if (opt & OPT_R) /* release on quit */
1859 perform_d6_release(&srv6_buf, requested_ipv6);
1860 retval = 0;
1861 ret:
Denys Vlasenkobcb1fc32019-05-26 15:01:13 +02001862 /*if (client_data.pidfile) - remove_pidfile has its own check */
1863 remove_pidfile(client_data.pidfile);
Denys Vlasenko9ba75042011-11-07 15:55:39 +01001864 return retval;
Denys Vlasenkoabe248b2019-05-15 14:19:46 +02001865}