diff options
Diffstat (limited to 'networking/udhcp/d6_dhcpc.c')
-rw-r--r-- | networking/udhcp/d6_dhcpc.c | 128 |
1 files changed, 101 insertions, 27 deletions
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index 64339c9b5..18a104c61 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c | |||
@@ -54,7 +54,9 @@ static const char udhcpc6_longopts[] ALIGN1 = | |||
54 | "request-option\0" Required_argument "O" | 54 | "request-option\0" Required_argument "O" |
55 | "no-default-options\0" No_argument "o" | 55 | "no-default-options\0" No_argument "o" |
56 | "foreground\0" No_argument "f" | 56 | "foreground\0" No_argument "f" |
57 | USE_FOR_MMU( | ||
57 | "background\0" No_argument "b" | 58 | "background\0" No_argument "b" |
59 | ) | ||
58 | /// IF_FEATURE_UDHCPC_ARPING("arping\0" No_argument "a") | 60 | /// IF_FEATURE_UDHCPC_ARPING("arping\0" No_argument "a") |
59 | IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P") | 61 | IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P") |
60 | ; | 62 | ; |
@@ -86,6 +88,19 @@ enum { | |||
86 | IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,) | 88 | IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,) |
87 | }; | 89 | }; |
88 | 90 | ||
91 | static const char opt_req[] = { | ||
92 | (D6_OPT_ORO >> 8), (D6_OPT_ORO & 0xff), | ||
93 | 0, 6, | ||
94 | (D6_OPT_DNS_SERVERS >> 8), (D6_OPT_DNS_SERVERS & 0xff), | ||
95 | (D6_OPT_DOMAIN_LIST >> 8), (D6_OPT_DOMAIN_LIST & 0xff), | ||
96 | (D6_OPT_CLIENT_FQDN >> 8), (D6_OPT_CLIENT_FQDN & 0xff) | ||
97 | }; | ||
98 | |||
99 | static const char opt_fqdn_req[] = { | ||
100 | (D6_OPT_CLIENT_FQDN >> 8), (D6_OPT_CLIENT_FQDN & 0xff), | ||
101 | 0, 2, | ||
102 | 0, 0 | ||
103 | }; | ||
89 | 104 | ||
90 | /*** Utility functions ***/ | 105 | /*** Utility functions ***/ |
91 | 106 | ||
@@ -107,8 +122,8 @@ static void *d6_find_option(uint8_t *option, uint8_t *option_end, unsigned code) | |||
107 | /* Does its code match? */ | 122 | /* Does its code match? */ |
108 | if (option[1] == code) | 123 | if (option[1] == code) |
109 | return option; /* yes! */ | 124 | return option; /* yes! */ |
110 | option += option[3] + 4; | ||
111 | len_m4 -= option[3] + 4; | 125 | len_m4 -= option[3] + 4; |
126 | option += option[3] + 4; | ||
112 | } | 127 | } |
113 | return NULL; | 128 | return NULL; |
114 | } | 129 | } |
@@ -139,8 +154,10 @@ static char** new_env(void) | |||
139 | /* put all the parameters into the environment */ | 154 | /* put all the parameters into the environment */ |
140 | static void option_to_env(uint8_t *option, uint8_t *option_end) | 155 | static void option_to_env(uint8_t *option, uint8_t *option_end) |
141 | { | 156 | { |
157 | char *dlist, *ptr; | ||
142 | /* "length minus 4" */ | 158 | /* "length minus 4" */ |
143 | int len_m4 = option_end - option - 4; | 159 | int len_m4 = option_end - option - 4; |
160 | int olen, ooff; | ||
144 | while (len_m4 >= 0) { | 161 | while (len_m4 >= 0) { |
145 | uint32_t v32; | 162 | uint32_t v32; |
146 | char ipv6str[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")]; | 163 | char ipv6str[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")]; |
@@ -217,9 +234,54 @@ static void option_to_env(uint8_t *option, uint8_t *option_end) | |||
217 | 234 | ||
218 | sprint_nip6(ipv6str, option + 4 + 4 + 1); | 235 | sprint_nip6(ipv6str, option + 4 + 4 + 1); |
219 | *new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4])); | 236 | *new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4])); |
237 | break; | ||
238 | case D6_OPT_DNS_SERVERS: | ||
239 | olen = ((option[2] << 8) | option[3]) / 16; | ||
240 | dlist = ptr = malloc (4 + olen * 40 - 1); | ||
241 | |||
242 | memcpy (ptr, "dns=", 4); | ||
243 | ptr += 4; | ||
244 | ooff = 0; | ||
245 | |||
246 | while (olen--) { | ||
247 | sprint_nip6(ptr, option + 4 + ooff); | ||
248 | ptr += 39; | ||
249 | ooff += 16; | ||
250 | if (olen) | ||
251 | *ptr++ = ' '; | ||
252 | } | ||
253 | |||
254 | *new_env() = dlist; | ||
255 | |||
256 | break; | ||
257 | case D6_OPT_DOMAIN_LIST: | ||
258 | dlist = dname_dec(option + 4, (option[2] << 8) | option[3], "search="); | ||
259 | if (!dlist) | ||
260 | break; | ||
261 | *new_env() = dlist; | ||
262 | break; | ||
263 | case D6_OPT_CLIENT_FQDN: | ||
264 | // Work around broken ISC DHCPD6 | ||
265 | if (option[4] & 0xf8) { | ||
266 | olen = ((option[2] << 8) | option[3]); | ||
267 | dlist = xmalloc(olen); | ||
268 | //fixme: | ||
269 | //- explain | ||
270 | //- add len error check | ||
271 | //- merge two allocs into one | ||
272 | memcpy(dlist, option + 4, olen); | ||
273 | *new_env() = xasprintf("fqdn=%s", dlist); | ||
274 | free(dlist); | ||
275 | break; | ||
276 | } | ||
277 | dlist = dname_dec(option + 5, ((option[2] << 8) | option[3]) - 1, "fqdn="); | ||
278 | if (!dlist) | ||
279 | break; | ||
280 | *new_env() = dlist; | ||
281 | break; | ||
220 | } | 282 | } |
221 | option += 4 + option[3]; | ||
222 | len_m4 -= 4 + option[3]; | 283 | len_m4 -= 4 + option[3]; |
284 | option += 4 + option[3]; | ||
223 | } | 285 | } |
224 | } | 286 | } |
225 | 287 | ||
@@ -311,7 +373,7 @@ static int d6_mcast_from_client_config_ifindex(struct d6_packet *packet, uint8_t | |||
311 | 373 | ||
312 | return d6_send_raw_packet( | 374 | return d6_send_raw_packet( |
313 | packet, (end - (uint8_t*) packet), | 375 | packet, (end - (uint8_t*) packet), |
314 | /*src*/ NULL, CLIENT_PORT6, | 376 | /*src*/ &client6_data.ll_ip6, CLIENT_PORT6, |
315 | /*dst*/ (struct in6_addr*)FF02__1_2, SERVER_PORT6, MAC_BCAST_ADDR, | 377 | /*dst*/ (struct in6_addr*)FF02__1_2, SERVER_PORT6, MAC_BCAST_ADDR, |
316 | client_config.ifindex | 378 | client_config.ifindex |
317 | ); | 379 | ); |
@@ -423,6 +485,10 @@ static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ip | |||
423 | } | 485 | } |
424 | opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, len); | 486 | opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, len); |
425 | 487 | ||
488 | /* Request additional options */ | ||
489 | opt_ptr = d6_store_blob(opt_ptr, &opt_req, sizeof(opt_req)); | ||
490 | opt_ptr = d6_store_blob(opt_ptr, &opt_fqdn_req, sizeof(opt_fqdn_req)); | ||
491 | |||
426 | /* Add options: | 492 | /* Add options: |
427 | * "param req" option according to -O, options specified with -x | 493 | * "param req" option according to -O, options specified with -x |
428 | */ | 494 | */ |
@@ -476,6 +542,10 @@ static NOINLINE int send_d6_select(uint32_t xid) | |||
476 | /* IA NA (contains requested IP) */ | 542 | /* IA NA (contains requested IP) */ |
477 | opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2); | 543 | opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2); |
478 | 544 | ||
545 | /* Request additional options */ | ||
546 | opt_ptr = d6_store_blob(opt_ptr, &opt_req, sizeof(opt_req)); | ||
547 | opt_ptr = d6_store_blob(opt_ptr, &opt_fqdn_req, sizeof(opt_fqdn_req)); | ||
548 | |||
479 | /* Add options: | 549 | /* Add options: |
480 | * "param req" option according to -O, options specified with -x | 550 | * "param req" option according to -O, options specified with -x |
481 | */ | 551 | */ |
@@ -555,7 +625,8 @@ static NOINLINE int send_d6_renew(uint32_t xid, struct in6_addr *server_ipv6, st | |||
555 | return d6_send_kernel_packet( | 625 | return d6_send_kernel_packet( |
556 | &packet, (opt_ptr - (uint8_t*) &packet), | 626 | &packet, (opt_ptr - (uint8_t*) &packet), |
557 | our_cur_ipv6, CLIENT_PORT6, | 627 | our_cur_ipv6, CLIENT_PORT6, |
558 | server_ipv6, SERVER_PORT6 | 628 | server_ipv6, SERVER_PORT6, |
629 | client_config.ifindex | ||
559 | ); | 630 | ); |
560 | return d6_mcast_from_client_config_ifindex(&packet, opt_ptr); | 631 | return d6_mcast_from_client_config_ifindex(&packet, opt_ptr); |
561 | } | 632 | } |
@@ -577,15 +648,14 @@ static int send_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cu | |||
577 | return d6_send_kernel_packet( | 648 | return d6_send_kernel_packet( |
578 | &packet, (opt_ptr - (uint8_t*) &packet), | 649 | &packet, (opt_ptr - (uint8_t*) &packet), |
579 | our_cur_ipv6, CLIENT_PORT6, | 650 | our_cur_ipv6, CLIENT_PORT6, |
580 | server_ipv6, SERVER_PORT6 | 651 | server_ipv6, SERVER_PORT6, |
652 | client_config.ifindex | ||
581 | ); | 653 | ); |
582 | } | 654 | } |
583 | 655 | ||
584 | /* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */ | 656 | /* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */ |
585 | /* NOINLINE: limit stack usage in caller */ | 657 | /* NOINLINE: limit stack usage in caller */ |
586 | static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6 | 658 | static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6, struct d6_packet *d6_pkt, int fd) |
587 | UNUSED_PARAM | ||
588 | , struct d6_packet *d6_pkt, int fd) | ||
589 | { | 659 | { |
590 | int bytes; | 660 | int bytes; |
591 | struct ip6_udp_d6_packet packet; | 661 | struct ip6_udp_d6_packet packet; |
@@ -634,6 +704,9 @@ static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6 | |||
634 | // return -2; | 704 | // return -2; |
635 | // } | 705 | // } |
636 | 706 | ||
707 | if (peer_ipv6) | ||
708 | *peer_ipv6 = packet.ip6.ip6_src; /* struct copy */ | ||
709 | |||
637 | log1("received %s", "a packet"); | 710 | log1("received %s", "a packet"); |
638 | d6_dump_packet(&packet.data); | 711 | d6_dump_packet(&packet.data); |
639 | 712 | ||
@@ -935,9 +1008,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) | |||
935 | int timeout; /* must be signed */ | 1008 | int timeout; /* must be signed */ |
936 | unsigned already_waited_sec; | 1009 | unsigned already_waited_sec; |
937 | unsigned opt; | 1010 | unsigned opt; |
938 | int max_fd; | ||
939 | int retval; | 1011 | int retval; |
940 | fd_set rfds; | ||
941 | 1012 | ||
942 | setup_common_bufsiz(); | 1013 | setup_common_bufsiz(); |
943 | 1014 | ||
@@ -1003,11 +1074,13 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) | |||
1003 | /* now it looks similar to udhcpd's config file line: | 1074 | /* now it looks similar to udhcpd's config file line: |
1004 | * "optname optval", using the common routine: */ | 1075 | * "optname optval", using the common routine: */ |
1005 | udhcp_str2optset(optstr, &client_config.options); | 1076 | udhcp_str2optset(optstr, &client_config.options); |
1077 | if (colon) | ||
1078 | *colon = ':'; /* restore it for NOMMU reexec */ | ||
1006 | } | 1079 | } |
1007 | 1080 | ||
1008 | if (udhcp_read_interface(client_config.interface, | 1081 | if (d6_read_interface(client_config.interface, |
1009 | &client_config.ifindex, | 1082 | &client_config.ifindex, |
1010 | NULL, | 1083 | &client6_data.ll_ip6, |
1011 | client_config.client_mac) | 1084 | client_config.client_mac) |
1012 | ) { | 1085 | ) { |
1013 | return 1; | 1086 | return 1; |
@@ -1063,7 +1136,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) | |||
1063 | * "continue" statements in code below jump to the top of the loop. | 1136 | * "continue" statements in code below jump to the top of the loop. |
1064 | */ | 1137 | */ |
1065 | for (;;) { | 1138 | for (;;) { |
1066 | struct timeval tv; | 1139 | int tv; |
1140 | struct pollfd pfds[2]; | ||
1067 | struct d6_packet packet; | 1141 | struct d6_packet packet; |
1068 | uint8_t *packet_end; | 1142 | uint8_t *packet_end; |
1069 | /* silence "uninitialized!" warning */ | 1143 | /* silence "uninitialized!" warning */ |
@@ -1078,16 +1152,15 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) | |||
1078 | * to change_listen_mode(). Thus we open listen socket | 1152 | * to change_listen_mode(). Thus we open listen socket |
1079 | * BEFORE we send renew request (see "case BOUND:"). */ | 1153 | * BEFORE we send renew request (see "case BOUND:"). */ |
1080 | 1154 | ||
1081 | max_fd = udhcp_sp_fd_set(&rfds, sockfd); | 1155 | udhcp_sp_fd_set(pfds, sockfd); |
1082 | 1156 | ||
1083 | tv.tv_sec = timeout - already_waited_sec; | 1157 | tv = timeout - already_waited_sec; |
1084 | tv.tv_usec = 0; | ||
1085 | retval = 0; | 1158 | retval = 0; |
1086 | /* If we already timed out, fall through with retval = 0, else... */ | 1159 | /* If we already timed out, fall through with retval = 0, else... */ |
1087 | if ((int)tv.tv_sec > 0) { | 1160 | if (tv > 0) { |
1088 | log1("waiting on select %u seconds", (int)tv.tv_sec); | 1161 | log1("waiting on select %u seconds", tv); |
1089 | timestamp_before_wait = (unsigned)monotonic_sec(); | 1162 | timestamp_before_wait = (unsigned)monotonic_sec(); |
1090 | retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); | 1163 | retval = poll(pfds, 2, tv < INT_MAX/1000 ? tv * 1000 : INT_MAX); |
1091 | if (retval < 0) { | 1164 | if (retval < 0) { |
1092 | /* EINTR? A signal was caught, don't panic */ | 1165 | /* EINTR? A signal was caught, don't panic */ |
1093 | if (errno == EINTR) { | 1166 | if (errno == EINTR) { |
@@ -1108,13 +1181,14 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) | |||
1108 | * or if the status of the bridge changed). | 1181 | * or if the status of the bridge changed). |
1109 | * Refresh ifindex and client_mac: | 1182 | * Refresh ifindex and client_mac: |
1110 | */ | 1183 | */ |
1111 | if (udhcp_read_interface(client_config.interface, | 1184 | if (d6_read_interface(client_config.interface, |
1112 | &client_config.ifindex, | 1185 | &client_config.ifindex, |
1113 | NULL, | 1186 | &client6_data.ll_ip6, |
1114 | client_config.client_mac) | 1187 | client_config.client_mac) |
1115 | ) { | 1188 | ) { |
1116 | goto ret0; /* iface is gone? */ | 1189 | goto ret0; /* iface is gone? */ |
1117 | } | 1190 | } |
1191 | |||
1118 | memcpy(clientid_mac_ptr, client_config.client_mac, 6); | 1192 | memcpy(clientid_mac_ptr, client_config.client_mac, 6); |
1119 | 1193 | ||
1120 | /* We will restart the wait in any case */ | 1194 | /* We will restart the wait in any case */ |
@@ -1222,8 +1296,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) | |||
1222 | /* select() didn't timeout, something happened */ | 1296 | /* select() didn't timeout, something happened */ |
1223 | 1297 | ||
1224 | /* Is it a signal? */ | 1298 | /* Is it a signal? */ |
1225 | /* note: udhcp_sp_read checks FD_ISSET before reading */ | 1299 | /* note: udhcp_sp_read checks poll result before reading */ |
1226 | switch (udhcp_sp_read(&rfds)) { | 1300 | switch (udhcp_sp_read(pfds)) { |
1227 | case SIGUSR1: | 1301 | case SIGUSR1: |
1228 | client_config.first_secs = 0; /* make secs field count from 0 */ | 1302 | client_config.first_secs = 0; /* make secs field count from 0 */ |
1229 | already_waited_sec = 0; | 1303 | already_waited_sec = 0; |
@@ -1258,7 +1332,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) | |||
1258 | } | 1332 | } |
1259 | 1333 | ||
1260 | /* Is it a packet? */ | 1334 | /* Is it a packet? */ |
1261 | if (listen_mode == LISTEN_NONE || !FD_ISSET(sockfd, &rfds)) | 1335 | if (listen_mode == LISTEN_NONE || !pfds[1].revents) |
1262 | continue; /* no */ | 1336 | continue; /* no */ |
1263 | 1337 | ||
1264 | { | 1338 | { |
@@ -1307,7 +1381,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) | |||
1307 | struct d6_option *option, *iaaddr; | 1381 | struct d6_option *option, *iaaddr; |
1308 | type_is_ok: | 1382 | type_is_ok: |
1309 | option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE); | 1383 | option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE); |
1310 | if (option && option->data[4] != 0) { | 1384 | if (option && (option->data[0] | option->data[1]) != 0) { |
1311 | /* return to init state */ | 1385 | /* return to init state */ |
1312 | bb_error_msg("received DHCP NAK (%u)", option->data[4]); | 1386 | bb_error_msg("received DHCP NAK (%u)", option->data[4]); |
1313 | d6_run_script(&packet, "nak"); | 1387 | d6_run_script(&packet, "nak"); |
@@ -1460,8 +1534,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) | |||
1460 | if (lease_seconds < 0x10) | 1534 | if (lease_seconds < 0x10) |
1461 | lease_seconds = 0x10; | 1535 | lease_seconds = 0x10; |
1462 | /// TODO: check for 0 lease time? | 1536 | /// TODO: check for 0 lease time? |
1463 | if (lease_seconds >= 0x10000000) | 1537 | if (lease_seconds > 0x7fffffff / 1000) |
1464 | lease_seconds = 0x0fffffff; | 1538 | lease_seconds = 0x7fffffff / 1000; |
1465 | /* enter bound state */ | 1539 | /* enter bound state */ |
1466 | timeout = lease_seconds / 2; | 1540 | timeout = lease_seconds / 2; |
1467 | bb_error_msg("lease obtained, lease time %u", | 1541 | bb_error_msg("lease obtained, lease time %u", |