aboutsummaryrefslogtreecommitdiff
path: root/networking
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2012-03-23 12:12:03 +0000
committerRon Yorston <rmy@pobox.com>2012-03-23 12:12:03 +0000
commitb0f54743e36af163ae2530c381c485bb29df13dc (patch)
treecda4cfeaae6e47fe4f14c1b566092be4da9affc4 /networking
parent40514a0309939f2446f0d4ed9600cad5de396e7f (diff)
parentba88826c66411affc1da3614742b454654f7298a (diff)
downloadbusybox-w32-b0f54743e36af163ae2530c381c485bb29df13dc.tar.gz
busybox-w32-b0f54743e36af163ae2530c381c485bb29df13dc.tar.bz2
busybox-w32-b0f54743e36af163ae2530c381c485bb29df13dc.zip
Merge branch 'busybox' into merge
Conflicts: Makefile.flags
Diffstat (limited to 'networking')
-rw-r--r--networking/Config.src16
-rw-r--r--networking/ftpd.c3
-rw-r--r--networking/ftpgetput.c34
-rw-r--r--networking/httpd.c276
-rw-r--r--networking/httpd_indexcgi.c27
-rw-r--r--networking/ifupdown.c14
-rw-r--r--networking/ip.c13
-rw-r--r--networking/nc_bloaty.c43
-rw-r--r--networking/ntpd.c281
-rw-r--r--networking/tftp.c5
-rw-r--r--networking/udhcp/common.c88
-rw-r--r--networking/udhcp/common.h6
-rw-r--r--networking/udhcp/d6_common.h123
-rw-r--r--networking/udhcp/d6_dhcpc.c1483
-rw-r--r--networking/udhcp/d6_packet.c172
-rw-r--r--networking/udhcp/d6_socket.c34
-rw-r--r--networking/udhcp/dhcpc.c168
-rw-r--r--networking/udhcp/packet.c57
-rw-r--r--networking/vconfig.c102
-rw-r--r--networking/wget.c6
20 files changed, 2537 insertions, 414 deletions
diff --git a/networking/Config.src b/networking/Config.src
index 8aeba0ef9..fb7dca7d4 100644
--- a/networking/Config.src
+++ b/networking/Config.src
@@ -199,14 +199,22 @@ config FEATURE_HTTPD_BASIC_AUTH
199 help 199 help
200 Utilizes password settings from /etc/httpd.conf for basic 200 Utilizes password settings from /etc/httpd.conf for basic
201 authentication on a per url basis. 201 authentication on a per url basis.
202 Example for httpd.conf file:
203 /adm:toor:PaSsWd
202 204
203config FEATURE_HTTPD_AUTH_MD5 205config FEATURE_HTTPD_AUTH_MD5
204 bool "Support MD5 crypted passwords for http Authentication" 206 bool "Support MD5 crypted passwords for http Authentication"
205 default y 207 default y
206 depends on FEATURE_HTTPD_BASIC_AUTH 208 depends on FEATURE_HTTPD_BASIC_AUTH
207 help 209 help
208 Enables basic per URL authentication from /etc/httpd.conf 210 Enables encrypted passwords, and wildcard user/passwords
209 using md5 passwords. 211 in httpd.conf file.
212 User '*' means 'any system user name is ok',
213 password of '*' means 'use system password for this user'
214 Examples:
215 /adm:toor:$1$P/eKnWXS$aI1aPGxT.dJD5SzqAKWrF0
216 /adm:root:*
217 /wiki:*:*
210 218
211config FEATURE_HTTPD_CGI 219config FEATURE_HTTPD_CGI
212 bool "Support Common Gateway Interface (CGI)" 220 bool "Support Common Gateway Interface (CGI)"
@@ -223,8 +231,8 @@ config FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
223 help 231 help
224 This option enables support for running scripts through an 232 This option enables support for running scripts through an
225 interpreter. Turn this on if you want PHP scripts to work 233 interpreter. Turn this on if you want PHP scripts to work
226 properly. You need to supply an additional line in your httpd 234 properly. You need to supply an additional line in your
227 config file: 235 httpd.conf file:
228 *.php:/path/to/your/php 236 *.php:/path/to/your/php
229 237
230config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV 238config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
diff --git a/networking/ftpd.c b/networking/ftpd.c
index e38138c0a..1c97df564 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -1179,8 +1179,7 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
1179#endif 1179#endif
1180 1180
1181 if (argv[optind]) { 1181 if (argv[optind]) {
1182 xchdir(argv[optind]); 1182 xchroot(argv[optind]);
1183 chroot(".");
1184 } 1183 }
1185 1184
1186 //umask(077); - admin can set umask before starting us 1185 //umask(077); - admin can set umask before starting us
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c
index abdf94c45..8283366cc 100644
--- a/networking/ftpgetput.c
+++ b/networking/ftpgetput.c
@@ -16,37 +16,37 @@
16//usage:#define ftpget_trivial_usage 16//usage:#define ftpget_trivial_usage
17//usage: "[OPTIONS] HOST [LOCAL_FILE] REMOTE_FILE" 17//usage: "[OPTIONS] HOST [LOCAL_FILE] REMOTE_FILE"
18//usage:#define ftpget_full_usage "\n\n" 18//usage:#define ftpget_full_usage "\n\n"
19//usage: "Retrieve a remote file via FTP\n" 19//usage: "Download a file via FTP\n"
20//usage: IF_FEATURE_FTPGETPUT_LONG_OPTIONS( 20//usage: IF_FEATURE_FTPGETPUT_LONG_OPTIONS(
21//usage: "\n -c,--continue Continue previous transfer" 21//usage: "\n -c,--continue Continue previous transfer"
22//usage: "\n -v,--verbose Verbose" 22//usage: "\n -v,--verbose Verbose"
23//usage: "\n -u,--username Username" 23//usage: "\n -u,--username USER Username"
24//usage: "\n -p,--password Password" 24//usage: "\n -p,--password PASS Password"
25//usage: "\n -P,--port Port number" 25//usage: "\n -P,--port NUM Port"
26//usage: ) 26//usage: )
27//usage: IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS( 27//usage: IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS(
28//usage: "\n -c Continue previous transfer" 28//usage: "\n -c Continue previous transfer"
29//usage: "\n -v Verbose" 29//usage: "\n -v Verbose"
30//usage: "\n -u Username" 30//usage: "\n -u USER Username"
31//usage: "\n -p Password" 31//usage: "\n -p PASS Password"
32//usage: "\n -P Port number" 32//usage: "\n -P NUM Port"
33//usage: ) 33//usage: )
34//usage: 34//usage:
35//usage:#define ftpput_trivial_usage 35//usage:#define ftpput_trivial_usage
36//usage: "[OPTIONS] HOST [REMOTE_FILE] LOCAL_FILE" 36//usage: "[OPTIONS] HOST [REMOTE_FILE] LOCAL_FILE"
37//usage:#define ftpput_full_usage "\n\n" 37//usage:#define ftpput_full_usage "\n\n"
38//usage: "Store a local file on a remote machine via FTP\n" 38//usage: "Upload a file to a FTP server\n"
39//usage: IF_FEATURE_FTPGETPUT_LONG_OPTIONS( 39//usage: IF_FEATURE_FTPGETPUT_LONG_OPTIONS(
40//usage: "\n -v,--verbose Verbose" 40//usage: "\n -v,--verbose Verbose"
41//usage: "\n -u,--username Username" 41//usage: "\n -u,--username USER Username"
42//usage: "\n -p,--password Password" 42//usage: "\n -p,--password PASS Password"
43//usage: "\n -P,--port Port number" 43//usage: "\n -P,--port NUM Port"
44//usage: ) 44//usage: )
45//usage: IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS( 45//usage: IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS(
46//usage: "\n -v Verbose" 46//usage: "\n -v Verbose"
47//usage: "\n -u Username" 47//usage: "\n -u USER Username"
48//usage: "\n -p Password" 48//usage: "\n -p PASS Password"
49//usage: "\n -P Port number" 49//usage: "\n -P NUM Port number"
50//usage: ) 50//usage: )
51 51
52#include "libbb.h" 52#include "libbb.h"
diff --git a/networking/httpd.c b/networking/httpd.c
index 24482fe52..f233cb0ba 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -54,6 +54,8 @@
54 * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/ 54 * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
55 * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ 55 * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/
56 * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ 56 * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
57 * /adm:root:* # or user root, pwd from /etc/passwd on urls starting with /adm/
58 * /wiki:*:* # or any user from /etc/passwd with according pwd on urls starting with /wiki/
57 * .au:audio/basic # additional mime type for audio.au files 59 * .au:audio/basic # additional mime type for audio.au files
58 * *.php:/path/php # run xxx.php through an interpreter 60 * *.php:/path/php # run xxx.php through an interpreter
59 * 61 *
@@ -123,6 +125,14 @@
123//usage: "\n -d STRING URL decode STRING" 125//usage: "\n -d STRING URL decode STRING"
124 126
125#include "libbb.h" 127#include "libbb.h"
128#if ENABLE_PAM
129/* PAM may include <locale.h>. We may need to undefine bbox's stub define: */
130# undef setlocale
131/* For some obscure reason, PAM is not in pam/xxx, but in security/xxx.
132 * Apparently they like to confuse people. */
133# include <security/pam_appl.h>
134# include <security/pam_misc.h>
135#endif
126#if ENABLE_FEATURE_HTTPD_USE_SENDFILE 136#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
127# include <sys/sendfile.h> 137# include <sys/sendfile.h>
128#endif 138#endif
@@ -338,7 +348,7 @@ struct globals {
338#define range_len (G.range_len ) 348#define range_len (G.range_len )
339#else 349#else
340enum { 350enum {
341 range_start = 0, 351 range_start = -1,
342 range_end = MAXINT(off_t) - 1, 352 range_end = MAXINT(off_t) - 1,
343 range_len = MAXINT(off_t), 353 range_len = MAXINT(off_t),
344}; 354};
@@ -360,6 +370,7 @@ enum {
360#define INIT_G() do { \ 370#define INIT_G() do { \
361 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 371 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
362 IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \ 372 IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
373 IF_FEATURE_HTTPD_RANGES(range_start = -1;) \
363 bind_addr_or_port = "80"; \ 374 bind_addr_or_port = "80"; \
364 index_page = index_html; \ 375 index_page = index_html; \
365 file_size = -1; \ 376 file_size = -1; \
@@ -1255,18 +1266,21 @@ static void setenv1(const char *name, const char *value)
1255 * 1266 *
1256 * Parameters: 1267 * Parameters:
1257 * const char *url The requested URL (with leading /). 1268 * const char *url The requested URL (with leading /).
1269 * const char *orig_uri The original URI before rewriting (if any)
1258 * int post_len Length of the POST body. 1270 * int post_len Length of the POST body.
1259 * const char *cookie For set HTTP_COOKIE. 1271 * const char *cookie For set HTTP_COOKIE.
1260 * const char *content_type For set CONTENT_TYPE. 1272 * const char *content_type For set CONTENT_TYPE.
1261 */ 1273 */
1262static void send_cgi_and_exit( 1274static void send_cgi_and_exit(
1263 const char *url, 1275 const char *url,
1276 const char *orig_uri,
1264 const char *request, 1277 const char *request,
1265 int post_len, 1278 int post_len,
1266 const char *cookie, 1279 const char *cookie,
1267 const char *content_type) NORETURN; 1280 const char *content_type) NORETURN;
1268static void send_cgi_and_exit( 1281static void send_cgi_and_exit(
1269 const char *url, 1282 const char *url,
1283 const char *orig_uri,
1270 const char *request, 1284 const char *request,
1271 int post_len, 1285 int post_len,
1272 const char *cookie, 1286 const char *cookie,
@@ -1274,7 +1288,7 @@ static void send_cgi_and_exit(
1274{ 1288{
1275 struct fd_pair fromCgi; /* CGI -> httpd pipe */ 1289 struct fd_pair fromCgi; /* CGI -> httpd pipe */
1276 struct fd_pair toCgi; /* httpd -> CGI pipe */ 1290 struct fd_pair toCgi; /* httpd -> CGI pipe */
1277 char *script; 1291 char *script, *last_slash;
1278 int pid; 1292 int pid;
1279 1293
1280 /* Make a copy. NB: caller guarantees: 1294 /* Make a copy. NB: caller guarantees:
@@ -1288,22 +1302,25 @@ static void send_cgi_and_exit(
1288 */ 1302 */
1289 1303
1290 /* Check for [dirs/]script.cgi/PATH_INFO */ 1304 /* Check for [dirs/]script.cgi/PATH_INFO */
1291 script = (char*)url; 1305 last_slash = script = (char*)url;
1292 while ((script = strchr(script + 1, '/')) != NULL) { 1306 while ((script = strchr(script + 1, '/')) != NULL) {
1307 int dir;
1293 *script = '\0'; 1308 *script = '\0';
1294 if (!is_directory(url + 1, 1, NULL)) { 1309 dir = is_directory(url + 1, /*followlinks:*/ 1);
1310 *script = '/';
1311 if (!dir) {
1295 /* not directory, found script.cgi/PATH_INFO */ 1312 /* not directory, found script.cgi/PATH_INFO */
1296 *script = '/';
1297 break; 1313 break;
1298 } 1314 }
1299 *script = '/'; /* is directory, find next '/' */ 1315 /* is directory, find next '/' */
1316 last_slash = script;
1300 } 1317 }
1301 setenv1("PATH_INFO", script); /* set to /PATH_INFO or "" */ 1318 setenv1("PATH_INFO", script); /* set to /PATH_INFO or "" */
1302 setenv1("REQUEST_METHOD", request); 1319 setenv1("REQUEST_METHOD", request);
1303 if (g_query) { 1320 if (g_query) {
1304 putenv(xasprintf("%s=%s?%s", "REQUEST_URI", url, g_query)); 1321 putenv(xasprintf("%s=%s?%s", "REQUEST_URI", orig_uri, g_query));
1305 } else { 1322 } else {
1306 setenv1("REQUEST_URI", url); 1323 setenv1("REQUEST_URI", orig_uri);
1307 } 1324 }
1308 if (script != NULL) 1325 if (script != NULL)
1309 *script = '\0'; /* cut off /PATH_INFO */ 1326 *script = '\0'; /* cut off /PATH_INFO */
@@ -1377,7 +1394,7 @@ static void send_cgi_and_exit(
1377 log_and_exit(); 1394 log_and_exit();
1378 } 1395 }
1379 1396
1380 if (!pid) { 1397 if (pid == 0) {
1381 /* Child process */ 1398 /* Child process */
1382 char *argv[3]; 1399 char *argv[3];
1383 1400
@@ -1393,7 +1410,7 @@ static void send_cgi_and_exit(
1393 /* dup2(1, 2); */ 1410 /* dup2(1, 2); */
1394 1411
1395 /* Chdiring to script's dir */ 1412 /* Chdiring to script's dir */
1396 script = strrchr(url, '/'); 1413 script = last_slash;
1397 if (script != url) { /* paranoia */ 1414 if (script != url) { /* paranoia */
1398 *script = '\0'; 1415 *script = '\0';
1399 if (chdir(url + 1) != 0) { 1416 if (chdir(url + 1) != 0) {
@@ -1573,10 +1590,10 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1573 if (what == SEND_BODY /* err pages and ranges don't mix */ 1590 if (what == SEND_BODY /* err pages and ranges don't mix */
1574 || content_gzip /* we are sending compressed page: can't do ranges */ ///why? 1591 || content_gzip /* we are sending compressed page: can't do ranges */ ///why?
1575 ) { 1592 ) {
1576 range_start = 0; 1593 range_start = -1;
1577 } 1594 }
1578 range_len = MAXINT(off_t); 1595 range_len = MAXINT(off_t);
1579 if (range_start) { 1596 if (range_start >= 0) {
1580 if (!range_end) { 1597 if (!range_end) {
1581 range_end = file_size - 1; 1598 range_end = file_size - 1;
1582 } 1599 }
@@ -1584,7 +1601,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1584 || lseek(fd, range_start, SEEK_SET) != range_start 1601 || lseek(fd, range_start, SEEK_SET) != range_start
1585 ) { 1602 ) {
1586 lseek(fd, 0, SEEK_SET); 1603 lseek(fd, 0, SEEK_SET);
1587 range_start = 0; 1604 range_start = -1;
1588 } else { 1605 } else {
1589 range_len = range_end - range_start + 1; 1606 range_len = range_end - range_start + 1;
1590 send_headers(HTTP_PARTIAL_CONTENT); 1607 send_headers(HTTP_PARTIAL_CONTENT);
@@ -1607,7 +1624,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1607 break; /* fall back to read/write loop */ 1624 break; /* fall back to read/write loop */
1608 goto fin; 1625 goto fin;
1609 } 1626 }
1610 IF_FEATURE_HTTPD_RANGES(range_len -= sz;) 1627 IF_FEATURE_HTTPD_RANGES(range_len -= count;)
1611 if (count == 0 || range_len == 0) 1628 if (count == 0 || range_len == 0)
1612 log_and_exit(); 1629 log_and_exit();
1613 } 1630 }
@@ -1658,6 +1675,56 @@ static int checkPermIP(void)
1658} 1675}
1659 1676
1660#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 1677#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
1678
1679# if ENABLE_FEATURE_HTTPD_AUTH_MD5 && ENABLE_PAM
1680struct pam_userinfo {
1681 const char *name;
1682 const char *pw;
1683};
1684
1685static int pam_talker(int num_msg,
1686 const struct pam_message **msg,
1687 struct pam_response **resp,
1688 void *appdata_ptr)
1689{
1690 int i;
1691 struct pam_userinfo *userinfo = (struct pam_userinfo *) appdata_ptr;
1692 struct pam_response *response;
1693
1694 if (!resp || !msg || !userinfo)
1695 return PAM_CONV_ERR;
1696
1697 /* allocate memory to store response */
1698 response = xzalloc(num_msg * sizeof(*response));
1699
1700 /* copy values */
1701 for (i = 0; i < num_msg; i++) {
1702 const char *s;
1703
1704 switch (msg[i]->msg_style) {
1705 case PAM_PROMPT_ECHO_ON:
1706 s = userinfo->name;
1707 break;
1708 case PAM_PROMPT_ECHO_OFF:
1709 s = userinfo->pw;
1710 break;
1711 case PAM_ERROR_MSG:
1712 case PAM_TEXT_INFO:
1713 s = "";
1714 break;
1715 default:
1716 free(response);
1717 return PAM_CONV_ERR;
1718 }
1719 response[i].resp = xstrdup(s);
1720 if (PAM_SUCCESS != 0)
1721 response[i].resp_retcode = PAM_SUCCESS;
1722 }
1723 *resp = response;
1724 return PAM_SUCCESS;
1725}
1726# endif
1727
1661/* 1728/*
1662 * Config file entries are of the form "/<path>:<user>:<passwd>". 1729 * Config file entries are of the form "/<path>:<user>:<passwd>".
1663 * If config file has no prefix match for path, access is allowed. 1730 * If config file has no prefix match for path, access is allowed.
@@ -1667,7 +1734,7 @@ static int checkPermIP(void)
1667 * 1734 *
1668 * Returns 1 if user_and_passwd is OK. 1735 * Returns 1 if user_and_passwd is OK.
1669 */ 1736 */
1670static int check_user_passwd(const char *path, const char *user_and_passwd) 1737static int check_user_passwd(const char *path, char *user_and_passwd)
1671{ 1738{
1672 Htaccess *cur; 1739 Htaccess *cur;
1673 const char *prev = NULL; 1740 const char *prev = NULL;
@@ -1675,6 +1742,7 @@ static int check_user_passwd(const char *path, const char *user_and_passwd)
1675 for (cur = g_auth; cur; cur = cur->next) { 1742 for (cur = g_auth; cur; cur = cur->next) {
1676 const char *dir_prefix; 1743 const char *dir_prefix;
1677 size_t len; 1744 size_t len;
1745 int r;
1678 1746
1679 dir_prefix = cur->before_colon; 1747 dir_prefix = cur->before_colon;
1680 1748
@@ -1690,7 +1758,8 @@ static int check_user_passwd(const char *path, const char *user_and_passwd)
1690 len = strlen(dir_prefix); 1758 len = strlen(dir_prefix);
1691 if (len != 1 /* dir_prefix "/" matches all, don't need to check */ 1759 if (len != 1 /* dir_prefix "/" matches all, don't need to check */
1692 && (strncmp(dir_prefix, path, len) != 0 1760 && (strncmp(dir_prefix, path, len) != 0
1693 || (path[len] != '/' && path[len] != '\0')) 1761 || (path[len] != '/' && path[len] != '\0')
1762 )
1694 ) { 1763 ) {
1695 continue; 1764 continue;
1696 } 1765 }
@@ -1699,38 +1768,103 @@ static int check_user_passwd(const char *path, const char *user_and_passwd)
1699 prev = dir_prefix; 1768 prev = dir_prefix;
1700 1769
1701 if (ENABLE_FEATURE_HTTPD_AUTH_MD5) { 1770 if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
1702 char *md5_passwd; 1771 char *colon_after_user;
1772 const char *passwd;
1773# if ENABLE_FEATURE_SHADOWPASSWDS && !ENABLE_PAM
1774 char sp_buf[256];
1775# endif
1703 1776
1704 md5_passwd = strchr(cur->after_colon, ':'); 1777 colon_after_user = strchr(user_and_passwd, ':');
1705 if (md5_passwd && md5_passwd[1] == '$' && md5_passwd[2] == '1' 1778 if (!colon_after_user)
1706 && md5_passwd[3] == '$' && md5_passwd[4] 1779 goto bad_input;
1707 ) {
1708 char *encrypted;
1709 int r, user_len_p1;
1710 1780
1711 md5_passwd++; 1781 /* compare "user:" */
1712 user_len_p1 = md5_passwd - cur->after_colon; 1782 if (cur->after_colon[0] != '*'
1713 /* comparing "user:" */ 1783 && strncmp(cur->after_colon, user_and_passwd,
1714 if (strncmp(cur->after_colon, user_and_passwd, user_len_p1) != 0) { 1784 colon_after_user - user_and_passwd + 1) != 0
1785 ) {
1786 continue;
1787 }
1788 /* this cfg entry is '*' or matches username from peer */
1789
1790 passwd = strchr(cur->after_colon, ':');
1791 if (!passwd)
1792 goto bad_input;
1793 passwd++;
1794 if (passwd[0] == '*') {
1795# if ENABLE_PAM
1796 struct pam_userinfo userinfo;
1797 struct pam_conv conv_info = { &pam_talker, (void *) &userinfo };
1798 pam_handle_t *pamh;
1799
1800 *colon_after_user = '\0';
1801 userinfo.name = user_and_passwd;
1802 userinfo.pw = colon_after_user + 1;
1803 r = pam_start("httpd", user_and_passwd, &conv_info, &pamh) != PAM_SUCCESS;
1804 if (r == 0) {
1805 r = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS
1806 || pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS
1807 ;
1808 pam_end(pamh, PAM_SUCCESS);
1809 }
1810 *colon_after_user = ':';
1811 goto end_check_passwd;
1812# else
1813# if ENABLE_FEATURE_SHADOWPASSWDS
1814 /* Using _r function to avoid pulling in static buffers */
1815 struct spwd spw;
1816# endif
1817 struct passwd *pw;
1818
1819 *colon_after_user = '\0';
1820 pw = getpwnam(user_and_passwd);
1821 *colon_after_user = ':';
1822 if (!pw || !pw->pw_passwd)
1715 continue; 1823 continue;
1824 passwd = pw->pw_passwd;
1825# if ENABLE_FEATURE_SHADOWPASSWDS
1826 if ((passwd[0] == 'x' || passwd[0] == '*') && !passwd[1]) {
1827 /* getspnam_r may return 0 yet set result to NULL.
1828 * At least glibc 2.4 does this. Be extra paranoid here. */
1829 struct spwd *result = NULL;
1830 r = getspnam_r(pw->pw_name, &spw, sp_buf, sizeof(sp_buf), &result);
1831 if (r == 0 && result)
1832 passwd = result->sp_pwdp;
1716 } 1833 }
1834# endif
1835 /* In this case, passwd is ALWAYS encrypted:
1836 * it came from /etc/passwd or /etc/shadow!
1837 */
1838 goto check_encrypted;
1839# endif /* ENABLE_PAM */
1840 }
1841 /* Else: passwd is from httpd.conf, it is either plaintext or encrypted */
1717 1842
1843 if (passwd[0] == '$' && isdigit(passwd[1])) {
1844 char *encrypted;
1845 check_encrypted:
1846 /* encrypt pwd from peer and check match with local one */
1718 encrypted = pw_encrypt( 1847 encrypted = pw_encrypt(
1719 user_and_passwd + user_len_p1 /* cleartext pwd from user */, 1848 /* pwd (from peer): */ colon_after_user + 1,
1720 md5_passwd /*salt */, 1 /* cleanup */); 1849 /* salt: */ passwd,
1721 r = strcmp(encrypted, md5_passwd); 1850 /* cleanup: */ 0
1851 );
1852 r = strcmp(encrypted, passwd);
1722 free(encrypted); 1853 free(encrypted);
1723 if (r == 0) 1854 } else {
1724 goto set_remoteuser_var; /* Ok */ 1855 /* local passwd is from httpd.conf and it's plaintext */
1725 continue; 1856 r = strcmp(colon_after_user + 1, passwd);
1726 } 1857 }
1858 goto end_check_passwd;
1727 } 1859 }
1728 1860 bad_input:
1729 /* Comparing plaintext "user:pass" in one go */ 1861 /* Comparing plaintext "user:pass" in one go */
1730 if (strcmp(cur->after_colon, user_and_passwd) == 0) { 1862 r = strcmp(cur->after_colon, user_and_passwd);
1731 set_remoteuser_var: 1863 end_check_passwd:
1864 if (r == 0) {
1732 remoteuser = xstrndup(user_and_passwd, 1865 remoteuser = xstrndup(user_and_passwd,
1733 strchrnul(user_and_passwd, ':') - user_and_passwd); 1866 strchrnul(user_and_passwd, ':') - user_and_passwd
1867 );
1734 return 1; /* Ok */ 1868 return 1; /* Ok */
1735 } 1869 }
1736 } /* for */ 1870 } /* for */
@@ -1869,7 +2003,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
1869 /* NB: urlcopy ptr is never changed after this */ 2003 /* NB: urlcopy ptr is never changed after this */
1870 2004
1871 /* Extract url args if present */ 2005 /* Extract url args if present */
1872 g_query = NULL; 2006 /* g_query = NULL; - already is */
1873 tptr = strchr(urlcopy, '?'); 2007 tptr = strchr(urlcopy, '?');
1874 if (tptr) { 2008 if (tptr) {
1875 *tptr++ = '\0'; 2009 *tptr++ = '\0';
@@ -1889,34 +2023,40 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
1889 /* Algorithm stolen from libbb bb_simplify_path(), 2023 /* Algorithm stolen from libbb bb_simplify_path(),
1890 * but don't strdup, retain trailing slash, protect root */ 2024 * but don't strdup, retain trailing slash, protect root */
1891 urlp = tptr = urlcopy; 2025 urlp = tptr = urlcopy;
1892 do { 2026 for (;;) {
1893 if (*urlp == '/') { 2027 if (*urlp == '/') {
1894 /* skip duplicate (or initial) slash */ 2028 /* skip duplicate (or initial) slash */
1895 if (*tptr == '/') { 2029 if (*tptr == '/') {
1896 continue; 2030 goto next_char;
1897 } 2031 }
1898 if (*tptr == '.') { 2032 if (*tptr == '.') {
1899 /* skip extra "/./" */ 2033 if (tptr[1] == '.' && (tptr[2] == '/' || tptr[2] == '\0')) {
1900 if (tptr[1] == '/' || !tptr[1]) { 2034 /* "..": be careful */
1901 continue; 2035 /* protect root */
1902 } 2036 if (urlp == urlcopy)
1903 /* "..": be careful */
1904 if (tptr[1] == '.' && (tptr[2] == '/' || !tptr[2])) {
1905 ++tptr;
1906 if (urlp == urlcopy) /* protect root */
1907 send_headers_and_exit(HTTP_BAD_REQUEST); 2037 send_headers_and_exit(HTTP_BAD_REQUEST);
1908 while (*--urlp != '/') /* omit previous dir */; 2038 /* omit previous dir */
2039 while (*--urlp != '/')
1909 continue; 2040 continue;
2041 /* skip to "./" or ".<NUL>" */
2042 tptr++;
2043 }
2044 if (tptr[1] == '/' || tptr[1] == '\0') {
2045 /* skip extra "/./" */
2046 goto next_char;
1910 } 2047 }
1911 } 2048 }
1912 } 2049 }
1913 *++urlp = *tptr; 2050 *++urlp = *tptr;
1914 } while (*++tptr); 2051 if (*urlp == '\0')
1915 *++urlp = '\0'; /* terminate after last character */ 2052 break;
2053 next_char:
2054 tptr++;
2055 }
1916 2056
1917 /* If URL is a directory, add '/' */ 2057 /* If URL is a directory, add '/' */
1918 if (urlp[-1] != '/') { 2058 if (urlp[-1] != '/') {
1919 if (is_directory(urlcopy + 1, 1, NULL)) { 2059 if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) {
1920 found_moved_temporarily = urlcopy; 2060 found_moved_temporarily = urlcopy;
1921 } 2061 }
1922 } 2062 }
@@ -1930,7 +2070,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
1930 while (ip_allowed && (tptr = strchr(tptr + 1, '/')) != NULL) { 2070 while (ip_allowed && (tptr = strchr(tptr + 1, '/')) != NULL) {
1931 /* have path1/path2 */ 2071 /* have path1/path2 */
1932 *tptr = '\0'; 2072 *tptr = '\0';
1933 if (is_directory(urlcopy + 1, 1, NULL)) { 2073 if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) {
1934 /* may have subdir config */ 2074 /* may have subdir config */
1935 parse_conf(urlcopy + 1, SUBDIR_PARSE); 2075 parse_conf(urlcopy + 1, SUBDIR_PARSE);
1936 ip_allowed = checkPermIP(); 2076 ip_allowed = checkPermIP();
@@ -2029,11 +2169,11 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2029 s += sizeof("bytes=")-1; 2169 s += sizeof("bytes=")-1;
2030 range_start = BB_STRTOOFF(s, &s, 10); 2170 range_start = BB_STRTOOFF(s, &s, 10);
2031 if (s[0] != '-' || range_start < 0) { 2171 if (s[0] != '-' || range_start < 0) {
2032 range_start = 0; 2172 range_start = -1;
2033 } else if (s[1]) { 2173 } else if (s[1]) {
2034 range_end = BB_STRTOOFF(s+1, NULL, 10); 2174 range_end = BB_STRTOOFF(s+1, NULL, 10);
2035 if (errno || range_end < range_start) 2175 if (errno || range_end < range_start)
2036 range_start = 0; 2176 range_start = -1;
2037 } 2177 }
2038 } 2178 }
2039 } 2179 }
@@ -2067,10 +2207,10 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2067 } 2207 }
2068 2208
2069#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 2209#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
2070 /* Case: no "Authorization:" was seen, but page does require passwd. 2210 /* Case: no "Authorization:" was seen, but page might require passwd.
2071 * Check that with dummy user:pass */ 2211 * Check that with dummy user:pass */
2072 if (authorized < 0) 2212 if (authorized < 0)
2073 authorized = check_user_passwd(urlcopy, ":"); 2213 authorized = check_user_passwd(urlcopy, (char *) "");
2074 if (!authorized) 2214 if (!authorized)
2075 send_headers_and_exit(HTTP_UNAUTHORIZED); 2215 send_headers_and_exit(HTTP_UNAUTHORIZED);
2076#endif 2216#endif
@@ -2116,12 +2256,20 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2116 /* protect listing "cgi-bin/" */ 2256 /* protect listing "cgi-bin/" */
2117 send_headers_and_exit(HTTP_FORBIDDEN); 2257 send_headers_and_exit(HTTP_FORBIDDEN);
2118 } 2258 }
2119 send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type); 2259 send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type);
2120 } 2260 }
2121#endif 2261#endif
2122 2262
2123 if (urlp[-1] == '/') 2263 if (urlp[-1] == '/') {
2264 /* When index_page string is appended to <dir>/ URL, it overwrites
2265 * the query string. If we fall back to call /cgi-bin/index.cgi,
2266 * query string would be lost and not available to the CGI.
2267 * Work around it by making a deep copy.
2268 */
2269 if (ENABLE_FEATURE_HTTPD_CGI)
2270 g_query = xstrdup(g_query); /* ok for NULL too */
2124 strcpy(urlp, index_page); 2271 strcpy(urlp, index_page);
2272 }
2125 if (stat(tptr, &sb) == 0) { 2273 if (stat(tptr, &sb) == 0) {
2126#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 2274#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
2127 char *suffix = strrchr(tptr, '.'); 2275 char *suffix = strrchr(tptr, '.');
@@ -2129,7 +2277,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2129 Htaccess *cur; 2277 Htaccess *cur;
2130 for (cur = script_i; cur; cur = cur->next) { 2278 for (cur = script_i; cur; cur = cur->next) {
2131 if (strcmp(cur->before_colon + 1, suffix) == 0) { 2279 if (strcmp(cur->before_colon + 1, suffix) == 0) {
2132 send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type); 2280 send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type);
2133 } 2281 }
2134 } 2282 }
2135 } 2283 }
@@ -2142,9 +2290,8 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2142 /* It's a dir URL and there is no index.html 2290 /* It's a dir URL and there is no index.html
2143 * Try cgi-bin/index.cgi */ 2291 * Try cgi-bin/index.cgi */
2144 if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) { 2292 if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
2145 urlp[0] = '\0'; 2293 urlp[0] = '\0'; /* remove index_page */
2146 g_query = urlcopy; 2294 send_cgi_and_exit("/cgi-bin/index.cgi", urlcopy, prequest, length, cookie, content_type);
2147 send_cgi_and_exit("/cgi-bin/index.cgi", prequest, length, cookie, content_type);
2148 } 2295 }
2149 } 2296 }
2150 /* else fall through to send_file, it errors out if open fails: */ 2297 /* else fall through to send_file, it errors out if open fails: */
@@ -2243,6 +2390,7 @@ static void mini_httpd_nommu(int server_socket, int argc, char **argv)
2243 /* Run a copy of ourself in inetd mode */ 2390 /* Run a copy of ourself in inetd mode */
2244 re_exec(argv_copy); 2391 re_exec(argv_copy);
2245 } 2392 }
2393 argv_copy[0][0] &= 0x7f;
2246 /* parent, or vfork failed */ 2394 /* parent, or vfork failed */
2247 close(n); 2395 close(n);
2248 } /* while (1) */ 2396 } /* while (1) */
@@ -2352,7 +2500,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2352 salt[1] = '1'; 2500 salt[1] = '1';
2353 salt[2] = '$'; 2501 salt[2] = '$';
2354 crypt_make_salt(salt + 3, 4); 2502 crypt_make_salt(salt + 3, 4);
2355 puts(pw_encrypt(pass, salt, 1)); 2503 puts(pw_encrypt(pass, salt, /*cleanup:*/ 0));
2356 return 0; 2504 return 0;
2357 } 2505 }
2358#endif 2506#endif
diff --git a/networking/httpd_indexcgi.c b/networking/httpd_indexcgi.c
index 7e0225e19..d732cd4f8 100644
--- a/networking/httpd_indexcgi.c
+++ b/networking/httpd_indexcgi.c
@@ -221,20 +221,25 @@ int main(int argc, char *argv[])
221 unsigned long long size_total; 221 unsigned long long size_total;
222 int odd; 222 int odd;
223 DIR *dirp; 223 DIR *dirp;
224 char *QUERY_STRING; 224 char *location;
225 225
226 QUERY_STRING = getenv("QUERY_STRING"); 226 location = getenv("REQUEST_URI");
227 if (!QUERY_STRING 227 if (!location)
228 || QUERY_STRING[0] != '/' 228 return 1;
229 || strstr(QUERY_STRING, "//") 229
230 || strstr(QUERY_STRING, "/../") 230 /* drop URL arguments if any */
231 || strcmp(strrchr(QUERY_STRING, '/'), "/..") == 0 231 strchrnul(location, '?')[0] = '\0';
232
233 if (location[0] != '/'
234 || strstr(location, "//")
235 || strstr(location, "/../")
236 || strcmp(strrchr(location, '/'), "/..") == 0
232 ) { 237 ) {
233 return 1; 238 return 1;
234 } 239 }
235 240
236 if (chdir("..") 241 if (chdir("..")
237 || (QUERY_STRING[1] && chdir(QUERY_STRING + 1)) 242 || (location[1] && chdir(location + 1))
238 ) { 243 ) {
239 return 1; 244 return 1;
240 } 245 }
@@ -271,14 +276,14 @@ int main(int argc, char *argv[])
271 "\r\n" /* Mandatory empty line after headers */ 276 "\r\n" /* Mandatory empty line after headers */
272 "<html><head><title>Index of "); 277 "<html><head><title>Index of ");
273 /* Guard against directories with &, > etc */ 278 /* Guard against directories with &, > etc */
274 fmt_html(QUERY_STRING); 279 fmt_html(location);
275 fmt_str( 280 fmt_str(
276 "</title>\n" 281 "</title>\n"
277 STYLE_STR 282 STYLE_STR
278 "</head>" "\n" 283 "</head>" "\n"
279 "<body>" "\n" 284 "<body>" "\n"
280 "<h1>Index of "); 285 "<h1>Index of ");
281 fmt_html(QUERY_STRING); 286 fmt_html(location);
282 fmt_str( 287 fmt_str(
283 "</h1>" "\n" 288 "</h1>" "\n"
284 "<table>" "\n" 289 "<table>" "\n"
diff --git a/networking/ifupdown.c b/networking/ifupdown.c
index 5946323d0..dfda20670 100644
--- a/networking/ifupdown.c
+++ b/networking/ifupdown.c
@@ -403,11 +403,11 @@ static int FAST_FUNC static_up6(struct interface_defn_t *ifd, execfn *exec)
403 result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec); 403 result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec);
404 result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec); 404 result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
405 /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */ 405 /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */
406 result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec); 406 result += execute("[[ip route add ::/0 via %gateway%]][[ prio %metric%]]", ifd, exec);
407# else 407# else
408 result = execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd, exec); 408 result = execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd, exec);
409 result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec); 409 result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec);
410 result += execute("[[route -A inet6 add ::/0 gw %gateway%]]", ifd, exec); 410 result += execute("[[route -A inet6 add ::/0 gw %gateway%[[ metric %metric%]]]]", ifd, exec);
411# endif 411# endif
412 return ((result == 3) ? 3 : 0); 412 return ((result == 3) ? 3 : 0);
413} 413}
@@ -490,7 +490,7 @@ static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec)
490 result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] " 490 result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] "
491 "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec); 491 "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec);
492 result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec); 492 result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
493 result += execute("[[ip route add default via %gateway% dev %iface%]]", ifd, exec); 493 result += execute("[[ip route add default via %gateway% dev %iface%[[ prio %metric%]]]]", ifd, exec);
494 return ((result == 3) ? 3 : 0); 494 return ((result == 3) ? 3 : 0);
495# else 495# else
496 /* ifconfig said to set iface up before it processes hw %hwaddress%, 496 /* ifconfig said to set iface up before it processes hw %hwaddress%,
@@ -500,7 +500,7 @@ static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec)
500 result += execute("ifconfig %iface% %address% netmask %netmask%" 500 result += execute("ifconfig %iface% %address% netmask %netmask%"
501 "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ", 501 "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
502 ifd, exec); 502 ifd, exec);
503 result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec); 503 result += execute("[[route add default gw %gateway%[[ metric %metric%]] %iface%]]", ifd, exec);
504 return ((result == 3) ? 3 : 0); 504 return ((result == 3) ? 3 : 0);
505# endif 505# endif
506} 506}
@@ -1311,9 +1311,9 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
1311 llist_t *state_list = read_iface_state(); 1311 llist_t *state_list = read_iface_state();
1312 llist_t *iface_state = find_iface_state(state_list, iface); 1312 llist_t *iface_state = find_iface_state(state_list, iface);
1313 1313
1314 if (cmds == iface_up) { 1314 if (cmds == iface_up && !any_failures) {
1315 char * const newiface = xasprintf("%s=%s", iface, liface); 1315 char *newiface = xasprintf("%s=%s", iface, liface);
1316 if (iface_state == NULL) { 1316 if (!iface_state) {
1317 llist_add_to_end(&state_list, newiface); 1317 llist_add_to_end(&state_list, newiface);
1318 } else { 1318 } else {
1319 free(iface_state->data); 1319 free(iface_state->data);
diff --git a/networking/ip.c b/networking/ip.c
index fb2f5e2da..98fe621b1 100644
--- a/networking/ip.c
+++ b/networking/ip.c
@@ -50,16 +50,15 @@
50//usage: "iplink show [DEVICE]" 50//usage: "iplink show [DEVICE]"
51//usage: 51//usage:
52//usage:#define iproute_trivial_usage 52//usage:#define iproute_trivial_usage
53//usage: "{ list | flush | { add | del | change | append |\n" 53//usage: "{ list | flush | add | del | change | append |\n"
54//usage: " replace | monitor } ROUTE }" 54//usage: " replace | test } ROUTE"
55//usage:#define iproute_full_usage "\n\n" 55//usage:#define iproute_full_usage "\n\n"
56//usage: "iproute { list | flush } SELECTOR\n" 56//usage: "iproute { list | flush } SELECTOR\n"
57//usage: "iproute get ADDRESS [from ADDRESS iif STRING]\n" 57//usage: "iproute get ADDRESS [from ADDRESS iif STRING]\n"
58//usage: " [oif STRING] [tos TOS]\n" 58//usage: " [oif STRING] [tos TOS]\n"
59//usage: "iproute { add | del | change | append | replace | monitor } ROUTE\n" 59//usage: "iproute { add | del | change | append | replace | test } ROUTE\n"
60//usage: " SELECTOR := [root PREFIX] [match PREFIX] [proto RTPROTO]\n" 60//usage: " SELECTOR := [root PREFIX] [match PREFIX] [proto RTPROTO]\n"
61//usage: " ROUTE := [TYPE] PREFIX [tos TOS] [proto RTPROTO]\n" 61//usage: " ROUTE := [TYPE] PREFIX [tos TOS] [proto RTPROTO] [metric METRIC]"
62//usage: " [metric METRIC]"
63//usage: 62//usage:
64//usage:#define iprule_trivial_usage 63//usage:#define iprule_trivial_usage
65//usage: "{[list | add | del] RULE}" 64//usage: "{[list | add | del] RULE}"
diff --git a/networking/nc_bloaty.c b/networking/nc_bloaty.c
index d184f689b..62a025116 100644
--- a/networking/nc_bloaty.c
+++ b/networking/nc_bloaty.c
@@ -115,6 +115,7 @@ struct globals {
115 unsigned wrote_out; /* total stdout bytes */ 115 unsigned wrote_out; /* total stdout bytes */
116 unsigned wrote_net; /* total net bytes */ 116 unsigned wrote_net; /* total net bytes */
117#endif 117#endif
118 char *proggie0saved;
118 /* ouraddr is never NULL and goes through three states as we progress: 119 /* ouraddr is never NULL and goes through three states as we progress:
119 1 - local address before bind (IP/port possibly zero) 120 1 - local address before bind (IP/port possibly zero)
120 2 - local address after bind (port is nonzero) 121 2 - local address after bind (port is nonzero)
@@ -127,7 +128,6 @@ struct globals {
127 128
128 jmp_buf jbuf; /* timer crud */ 129 jmp_buf jbuf; /* timer crud */
129 130
130 /* will malloc up the following globals: */
131 fd_set ding1; /* for select loop */ 131 fd_set ding1; /* for select loop */
132 fd_set ding2; 132 fd_set ding2;
133 char bigbuf_in[BIGSIZ]; /* data buffers */ 133 char bigbuf_in[BIGSIZ]; /* data buffers */
@@ -159,17 +159,16 @@ struct globals {
159 159
160/* Must match getopt32 call! */ 160/* Must match getopt32 call! */
161enum { 161enum {
162 OPT_h = (1 << 0), 162 OPT_n = (1 << 0),
163 OPT_n = (1 << 1), 163 OPT_p = (1 << 1),
164 OPT_p = (1 << 2), 164 OPT_s = (1 << 2),
165 OPT_s = (1 << 3), 165 OPT_u = (1 << 3),
166 OPT_u = (1 << 4), 166 OPT_v = (1 << 4),
167 OPT_v = (1 << 5), 167 OPT_w = (1 << 5),
168 OPT_w = (1 << 6), 168 OPT_l = (1 << 6) * ENABLE_NC_SERVER,
169 OPT_l = (1 << 7) * ENABLE_NC_SERVER, 169 OPT_i = (1 << (6+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
170 OPT_i = (1 << (7+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA, 170 OPT_o = (1 << (7+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
171 OPT_o = (1 << (8+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA, 171 OPT_z = (1 << (8+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
172 OPT_z = (1 << (9+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
173}; 172};
174 173
175#define o_nflag (option_mask32 & OPT_n) 174#define o_nflag (option_mask32 & OPT_n)
@@ -263,6 +262,8 @@ Debug("findline returning whole thing: %d", siz);
263static int doexec(char **proggie) NORETURN; 262static int doexec(char **proggie) NORETURN;
264static int doexec(char **proggie) 263static int doexec(char **proggie)
265{ 264{
265 if (G.proggie0saved)
266 proggie[0] = G.proggie0saved;
266 xmove_fd(netfd, 0); 267 xmove_fd(netfd, 0);
267 dup2(0, 1); 268 dup2(0, 1);
268 /* dup2(0, 2); - do we *really* want this? NO! 269 /* dup2(0, 2); - do we *really* want this? NO!
@@ -726,7 +727,7 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
726{ 727{
727 char *str_p, *str_s; 728 char *str_p, *str_s;
728 IF_NC_EXTRA(char *str_i, *str_o;) 729 IF_NC_EXTRA(char *str_i, *str_o;)
729 char *themdotted = themdotted; /* gcc */ 730 char *themdotted = themdotted; /* for compiler */
730 char **proggie; 731 char **proggie;
731 int x; 732 int x;
732 unsigned o_lport = 0; 733 unsigned o_lport = 0;
@@ -754,13 +755,27 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
754 proggie++; 755 proggie++;
755 goto e_found; 756 goto e_found;
756 } 757 }
758 /* -<other_opts>e PROG [ARGS] ? */
759 /* (aboriginal linux uses this form) */
760 if (proggie[0][0] == '-') {
761 char *optpos = *proggie + 1;
762 /* Skip all valid opts w/o params */
763 optpos = optpos + strspn(optpos, "nuv"IF_NC_SERVER("l")IF_NC_EXTRA("z"));
764 if (*optpos == 'e' && !optpos[1]) {
765 *optpos = '\0';
766 proggie++;
767 G.proggie0saved = *proggie;
768 *proggie = NULL; /* terminate argv for getopt32 */
769 goto e_found;
770 }
771 }
757 } 772 }
758 proggie = NULL; 773 proggie = NULL;
759 e_found: 774 e_found:
760 775
761 // -g -G -t -r deleted, unimplemented -a deleted too 776 // -g -G -t -r deleted, unimplemented -a deleted too
762 opt_complementary = "?2:vv:w+"; /* max 2 params; -v is a counter; -w N */ 777 opt_complementary = "?2:vv:w+"; /* max 2 params; -v is a counter; -w N */
763 getopt32(argv, "hnp:s:uvw:" IF_NC_SERVER("l") 778 getopt32(argv, "np:s:uvw:" IF_NC_SERVER("l")
764 IF_NC_EXTRA("i:o:z"), 779 IF_NC_EXTRA("i:o:z"),
765 &str_p, &str_s, &o_wait 780 &str_p, &str_s, &o_wait
766 IF_NC_EXTRA(, &str_i, &str_o), &o_verbose); 781 IF_NC_EXTRA(, &str_i, &str_o), &o_verbose);
diff --git a/networking/ntpd.c b/networking/ntpd.c
index 206af00c7..4d939458c 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -107,12 +107,15 @@
107#define FREQ_TOLERANCE 0.000015 /* frequency tolerance (15 PPM) */ 107#define FREQ_TOLERANCE 0.000015 /* frequency tolerance (15 PPM) */
108#define BURSTPOLL 0 /* initial poll */ 108#define BURSTPOLL 0 /* initial poll */
109#define MINPOLL 5 /* minimum poll interval. std ntpd uses 6 (6: 64 sec) */ 109#define MINPOLL 5 /* minimum poll interval. std ntpd uses 6 (6: 64 sec) */
110#define BIGPOLL 10 /* drop to lower poll at any trouble (10: 17 min) */ 110/* If offset > discipline_jitter * POLLADJ_GATE, and poll interval is >= 2^BIGPOLL,
111 * then it is decreased _at once_. (If < 2^BIGPOLL, it will be decreased _eventually_).
112 */
113#define BIGPOLL 10 /* 2^10 sec ~= 17 min */
111#define MAXPOLL 12 /* maximum poll interval (12: 1.1h, 17: 36.4h). std ntpd uses 17 */ 114#define MAXPOLL 12 /* maximum poll interval (12: 1.1h, 17: 36.4h). std ntpd uses 17 */
112/* Actively lower poll when we see such big offsets. 115/* Actively lower poll when we see such big offsets.
113 * With STEP_THRESHOLD = 0.125, it means we try to sync more aggressively 116 * With STEP_THRESHOLD = 0.125, it means we try to sync more aggressively
114 * if offset increases over 0.03 sec */ 117 * if offset increases over ~0.04 sec */
115#define POLLDOWN_OFFSET (STEP_THRESHOLD / 4) 118#define POLLDOWN_OFFSET (STEP_THRESHOLD / 3)
116#define MINDISP 0.01 /* minimum dispersion (sec) */ 119#define MINDISP 0.01 /* minimum dispersion (sec) */
117#define MAXDISP 16 /* maximum dispersion (sec) */ 120#define MAXDISP 16 /* maximum dispersion (sec) */
118#define MAXSTRAT 16 /* maximum stratum (infinity metric) */ 121#define MAXSTRAT 16 /* maximum stratum (infinity metric) */
@@ -124,17 +127,18 @@
124 127
125/* Poll-adjust threshold. 128/* Poll-adjust threshold.
126 * When we see that offset is small enough compared to discipline jitter, 129 * When we see that offset is small enough compared to discipline jitter,
127 * we grow a counter: += MINPOLL. When it goes over POLLADJ_LIMIT, 130 * we grow a counter: += MINPOLL. When counter goes over POLLADJ_LIMIT,
128 * we poll_exp++. If offset isn't small, counter -= poll_exp*2, 131 * we poll_exp++. If offset isn't small, counter -= poll_exp*2,
129 * and when it goes below -POLLADJ_LIMIT, we poll_exp-- 132 * and when it goes below -POLLADJ_LIMIT, we poll_exp--.
130 * (bumped from 30 to 36 since otherwise I often see poll_exp going *2* steps down) 133 * (Bumped from 30 to 40 since otherwise I often see poll_exp going *2* steps down)
131 */ 134 */
132#define POLLADJ_LIMIT 36 135#define POLLADJ_LIMIT 40
133/* If offset < POLLADJ_GATE * discipline_jitter, then we can increase 136/* If offset < discipline_jitter * POLLADJ_GATE, then we decide to increase
134 * poll interval (we think we can't improve timekeeping 137 * poll interval (we think we can't improve timekeeping
135 * by staying at smaller poll). 138 * by staying at smaller poll).
136 */ 139 */
137#define POLLADJ_GATE 4 140#define POLLADJ_GATE 4
141#define TIMECONST_HACK_GATE 2
138/* Compromise Allan intercept (sec). doc uses 1500, std ntpd uses 512 */ 142/* Compromise Allan intercept (sec). doc uses 1500, std ntpd uses 512 */
139#define ALLAN 512 143#define ALLAN 512
140/* PLL loop gain */ 144/* PLL loop gain */
@@ -208,8 +212,8 @@ typedef struct {
208} msg_t; 212} msg_t;
209 213
210typedef struct { 214typedef struct {
211 double d_recv_time;
212 double d_offset; 215 double d_offset;
216 double d_recv_time;
213 double d_dispersion; 217 double d_dispersion;
214} datapoint_t; 218} datapoint_t;
215 219
@@ -255,7 +259,7 @@ enum {
255 OPT_S = (1 << 6), 259 OPT_S = (1 << 6),
256 OPT_l = (1 << 7) * ENABLE_FEATURE_NTPD_SERVER, 260 OPT_l = (1 << 7) * ENABLE_FEATURE_NTPD_SERVER,
257 /* We hijack some bits for other purposes */ 261 /* We hijack some bits for other purposes */
258 OPT_qq = (1 << 8), 262 OPT_qq = (1 << 31),
259}; 263};
260 264
261struct globals { 265struct globals {
@@ -276,11 +280,12 @@ struct globals {
276 unsigned verbose; 280 unsigned verbose;
277 unsigned peer_cnt; 281 unsigned peer_cnt;
278 /* refid: 32-bit code identifying the particular server or reference clock 282 /* refid: 32-bit code identifying the particular server or reference clock
279 * in stratum 0 packets this is a four-character ASCII string, 283 * in stratum 0 packets this is a four-character ASCII string,
280 * called the kiss code, used for debugging and monitoring 284 * called the kiss code, used for debugging and monitoring
281 * in stratum 1 packets this is a four-character ASCII string 285 * in stratum 1 packets this is a four-character ASCII string
282 * assigned to the reference clock by IANA. Example: "GPS " 286 * assigned to the reference clock by IANA. Example: "GPS "
283 * in stratum 2+ packets, it's IPv4 address or 4 first bytes of MD5 hash of IPv6 287 * in stratum 2+ packets, it's IPv4 address or 4 first bytes
288 * of MD5 hash of IPv6
284 */ 289 */
285 uint32_t refid; 290 uint32_t refid;
286 uint8_t ntp_status; 291 uint8_t ntp_status;
@@ -289,27 +294,35 @@ struct globals {
289 * mains-frequency clock incrementing at 60 Hz is 16 ms, even when the 294 * mains-frequency clock incrementing at 60 Hz is 16 ms, even when the
290 * system clock hardware representation is to the nanosecond. 295 * system clock hardware representation is to the nanosecond.
291 * 296 *
292 * Delays, jitters of various kinds are clamper down to precision. 297 * Delays, jitters of various kinds are clamped down to precision.
293 * 298 *
294 * If precision_sec is too large, discipline_jitter gets clamped to it 299 * If precision_sec is too large, discipline_jitter gets clamped to it
295 * and if offset is much smaller than discipline_jitter, poll interval 300 * and if offset is smaller than discipline_jitter * POLLADJ_GATE, poll
296 * grows even though we really can benefit from staying at smaller one, 301 * interval grows even though we really can benefit from staying at
297 * collecting non-lagged datapoits and correcting the offset. 302 * smaller one, collecting non-lagged datapoits and correcting offset.
298 * (Lagged datapoits exist when poll_exp is large but we still have 303 * (Lagged datapoits exist when poll_exp is large but we still have
299 * systematic offset error - the time distance between datapoints 304 * systematic offset error - the time distance between datapoints
300 * is significat and older datapoints have smaller offsets. 305 * is significant and older datapoints have smaller offsets.
301 * This makes our offset estimation a bit smaller than reality) 306 * This makes our offset estimation a bit smaller than reality)
302 * Due to this effect, setting G_precision_sec close to 307 * Due to this effect, setting G_precision_sec close to
303 * STEP_THRESHOLD isn't such a good idea - offsets may grow 308 * STEP_THRESHOLD isn't such a good idea - offsets may grow
304 * too big and we will step. I observed it with -6. 309 * too big and we will step. I observed it with -6.
305 * 310 *
306 * OTOH, setting precision too small would result in futile attempts 311 * OTOH, setting precision_sec far too small would result in futile
307 * to syncronize to the unachievable precision. 312 * attempts to syncronize to an unachievable precision.
308 * 313 *
309 * -6 is 1/64 sec, -7 is 1/128 sec and so on. 314 * -6 is 1/64 sec, -7 is 1/128 sec and so on.
315 * -8 is 1/256 ~= 0.003906 (worked well for me --vda)
316 * -9 is 1/512 ~= 0.001953 (let's try this for some time)
310 */ 317 */
311#define G_precision_exp -8 318#define G_precision_exp -9
312#define G_precision_sec (1.0 / (1 << (- G_precision_exp))) 319 /*
320 * G_precision_exp is used only for construction outgoing packets.
321 * It's ok to set G_precision_sec to a slightly different value
322 * (One which is "nicer looking" in logs).
323 * Exact value would be (1.0 / (1 << (- G_precision_exp))):
324 */
325#define G_precision_sec 0.002
313 uint8_t stratum; 326 uint8_t stratum;
314 /* Bool. After set to 1, never goes back to 0: */ 327 /* Bool. After set to 1, never goes back to 0: */
315 smallint initial_poll_complete; 328 smallint initial_poll_complete;
@@ -327,6 +340,10 @@ struct globals {
327 double last_update_offset; // c.last 340 double last_update_offset; // c.last
328 double last_update_recv_time; // s.t 341 double last_update_recv_time; // s.t
329 double discipline_jitter; // c.jitter 342 double discipline_jitter; // c.jitter
343 /* Since we only compare it with ints, can simplify code
344 * by not making this variable floating point:
345 */
346 unsigned offset_to_jitter_ratio;
330 //double cluster_offset; // s.offset 347 //double cluster_offset; // s.offset
331 //double cluster_jitter; // s.jitter 348 //double cluster_jitter; // s.jitter
332#if !USING_KERNEL_PLL_LOOP 349#if !USING_KERNEL_PLL_LOOP
@@ -500,23 +517,34 @@ static void
500filter_datapoints(peer_t *p) 517filter_datapoints(peer_t *p)
501{ 518{
502 int i, idx; 519 int i, idx;
520 double sum, wavg;
521 datapoint_t *fdp;
522
523#if 0
524/* Simulations have shown that use of *averaged* offset for p->filter_offset
525 * is in fact worse than simply using last received one: with large poll intervals
526 * (>= 2048) averaging code uses offset values which are outdated by hours,
527 * and time/frequency correction goes totally wrong when fed essentially bogus offsets.
528 */
503 int got_newest; 529 int got_newest;
504 double minoff, maxoff, wavg, sum, w; 530 double minoff, maxoff, w;
505 double x = x; /* for compiler */ 531 double x = x; /* for compiler */
506 double oldest_off = oldest_off; 532 double oldest_off = oldest_off;
507 double oldest_age = oldest_age; 533 double oldest_age = oldest_age;
508 double newest_off = newest_off; 534 double newest_off = newest_off;
509 double newest_age = newest_age; 535 double newest_age = newest_age;
510 536
511 minoff = maxoff = p->filter_datapoint[0].d_offset; 537 fdp = p->filter_datapoint;
538
539 minoff = maxoff = fdp[0].d_offset;
512 for (i = 1; i < NUM_DATAPOINTS; i++) { 540 for (i = 1; i < NUM_DATAPOINTS; i++) {
513 if (minoff > p->filter_datapoint[i].d_offset) 541 if (minoff > fdp[i].d_offset)
514 minoff = p->filter_datapoint[i].d_offset; 542 minoff = fdp[i].d_offset;
515 if (maxoff < p->filter_datapoint[i].d_offset) 543 if (maxoff < fdp[i].d_offset)
516 maxoff = p->filter_datapoint[i].d_offset; 544 maxoff = fdp[i].d_offset;
517 } 545 }
518 546
519 idx = p->datapoint_idx; /* most recent datapoint */ 547 idx = p->datapoint_idx; /* most recent datapoint's index */
520 /* Average offset: 548 /* Average offset:
521 * Drop two outliers and take weighted average of the rest: 549 * Drop two outliers and take weighted average of the rest:
522 * most_recent/2 + older1/4 + older2/8 ... + older5/32 + older6/32 550 * most_recent/2 + older1/4 + older2/8 ... + older5/32 + older6/32
@@ -538,24 +566,24 @@ filter_datapoints(peer_t *p)
538 VERB4 { 566 VERB4 {
539 bb_error_msg("datapoint[%d]: off:%f disp:%f(%f) age:%f%s", 567 bb_error_msg("datapoint[%d]: off:%f disp:%f(%f) age:%f%s",
540 i, 568 i,
541 p->filter_datapoint[idx].d_offset, 569 fdp[idx].d_offset,
542 p->filter_datapoint[idx].d_dispersion, dispersion(&p->filter_datapoint[idx]), 570 fdp[idx].d_dispersion, dispersion(&fdp[idx]),
543 G.cur_time - p->filter_datapoint[idx].d_recv_time, 571 G.cur_time - fdp[idx].d_recv_time,
544 (minoff == p->filter_datapoint[idx].d_offset || maxoff == p->filter_datapoint[idx].d_offset) 572 (minoff == fdp[idx].d_offset || maxoff == fdp[idx].d_offset)
545 ? " (outlier by offset)" : "" 573 ? " (outlier by offset)" : ""
546 ); 574 );
547 } 575 }
548 576
549 sum += dispersion(&p->filter_datapoint[idx]) / (2 << i); 577 sum += dispersion(&fdp[idx]) / (2 << i);
550 578
551 if (minoff == p->filter_datapoint[idx].d_offset) { 579 if (minoff == fdp[idx].d_offset) {
552 minoff -= 1; /* so that we don't match it ever again */ 580 minoff -= 1; /* so that we don't match it ever again */
553 } else 581 } else
554 if (maxoff == p->filter_datapoint[idx].d_offset) { 582 if (maxoff == fdp[idx].d_offset) {
555 maxoff += 1; 583 maxoff += 1;
556 } else { 584 } else {
557 oldest_off = p->filter_datapoint[idx].d_offset; 585 oldest_off = fdp[idx].d_offset;
558 oldest_age = G.cur_time - p->filter_datapoint[idx].d_recv_time; 586 oldest_age = G.cur_time - fdp[idx].d_recv_time;
559 if (!got_newest) { 587 if (!got_newest) {
560 got_newest = 1; 588 got_newest = 1;
561 newest_off = oldest_off; 589 newest_off = oldest_off;
@@ -588,6 +616,32 @@ filter_datapoints(peer_t *p)
588 } 616 }
589 p->filter_offset = wavg; 617 p->filter_offset = wavg;
590 618
619#else
620
621 fdp = p->filter_datapoint;
622 idx = p->datapoint_idx; /* most recent datapoint's index */
623
624 /* filter_offset: simply use the most recent value */
625 p->filter_offset = fdp[idx].d_offset;
626
627 /* n-1
628 * --- dispersion(i)
629 * filter_dispersion = \ -------------
630 * / (i+1)
631 * --- 2
632 * i=0
633 */
634 wavg = 0;
635 sum = 0;
636 for (i = 0; i < NUM_DATAPOINTS; i++) {
637 sum += dispersion(&fdp[idx]) / (2 << i);
638 wavg += fdp[idx].d_offset;
639 idx = (idx - 1) & (NUM_DATAPOINTS - 1);
640 }
641 wavg /= NUM_DATAPOINTS;
642 p->filter_dispersion = sum;
643#endif
644
591 /* +----- -----+ ^ 1/2 645 /* +----- -----+ ^ 1/2
592 * | n-1 | 646 * | n-1 |
593 * | --- | 647 * | --- |
@@ -601,13 +655,13 @@ filter_datapoints(peer_t *p)
601 */ 655 */
602 sum = 0; 656 sum = 0;
603 for (i = 0; i < NUM_DATAPOINTS; i++) { 657 for (i = 0; i < NUM_DATAPOINTS; i++) {
604 sum += SQUARE(wavg - p->filter_datapoint[i].d_offset); 658 sum += SQUARE(wavg - fdp[i].d_offset);
605 } 659 }
606 sum = SQRT(sum / NUM_DATAPOINTS); 660 sum = SQRT(sum / NUM_DATAPOINTS);
607 p->filter_jitter = sum > G_precision_sec ? sum : G_precision_sec; 661 p->filter_jitter = sum > G_precision_sec ? sum : G_precision_sec;
608 662
609 VERB3 bb_error_msg("filter offset:%f(corr:%e) disp:%f jitter:%f", 663 VERB3 bb_error_msg("filter offset:%+f disp:%f jitter:%f",
610 p->filter_offset, x, 664 p->filter_offset,
611 p->filter_dispersion, 665 p->filter_dispersion,
612 p->filter_jitter); 666 p->filter_jitter);
613} 667}
@@ -622,7 +676,11 @@ reset_peer_stats(peer_t *p, double offset)
622 if (small_ofs) { 676 if (small_ofs) {
623 p->filter_datapoint[i].d_recv_time += offset; 677 p->filter_datapoint[i].d_recv_time += offset;
624 if (p->filter_datapoint[i].d_offset != 0) { 678 if (p->filter_datapoint[i].d_offset != 0) {
625 p->filter_datapoint[i].d_offset += offset; 679 p->filter_datapoint[i].d_offset -= offset;
680 //bb_error_msg("p->filter_datapoint[%d].d_offset %f -> %f",
681 // i,
682 // p->filter_datapoint[i].d_offset + offset,
683 // p->filter_datapoint[i].d_offset);
626 } 684 }
627 } else { 685 } else {
628 p->filter_datapoint[i].d_recv_time = G.cur_time; 686 p->filter_datapoint[i].d_recv_time = G.cur_time;
@@ -719,6 +777,12 @@ send_query_to_peer(peer_t *p)
719 free(local_lsa); 777 free(local_lsa);
720 } 778 }
721 779
780 /* Emit message _before_ attempted send. Think of a very short
781 * roundtrip networks: we need to go back to recv loop ASAP,
782 * to reduce delay. Printing messages after send works against that.
783 */
784 VERB1 bb_error_msg("sending query to %s", p->p_dotted);
785
722 /* 786 /*
723 * Send out a random 64-bit number as our transmit time. The NTP 787 * Send out a random 64-bit number as our transmit time. The NTP
724 * server will copy said number into the originate field on the 788 * server will copy said number into the originate field on the
@@ -746,7 +810,6 @@ send_query_to_peer(peer_t *p)
746 } 810 }
747 811
748 p->reachable_bits <<= 1; 812 p->reachable_bits <<= 1;
749 VERB1 bb_error_msg("sent query to %s", p->p_dotted);
750 set_next(p, RESPONSE_INTERVAL); 813 set_next(p, RESPONSE_INTERVAL);
751} 814}
752 815
@@ -808,22 +871,24 @@ step_time(double offset)
808{ 871{
809 llist_t *item; 872 llist_t *item;
810 double dtime; 873 double dtime;
811 struct timeval tv; 874 struct timeval tvc, tvn;
812 char buf[80]; 875 char buf[sizeof("yyyy-mm-dd hh:mm:ss") + /*paranoia:*/ 4];
813 time_t tval; 876 time_t tval;
814 877
815 gettimeofday(&tv, NULL); /* never fails */ 878 gettimeofday(&tvc, NULL); /* never fails */
816 dtime = offset + tv.tv_sec; 879 dtime = tvc.tv_sec + (1.0e-6 * tvc.tv_usec) + offset;
817 dtime += 1.0e-6 * tv.tv_usec; 880 d_to_tv(dtime, &tvn);
818 d_to_tv(dtime, &tv); 881 if (settimeofday(&tvn, NULL) == -1)
819
820 if (settimeofday(&tv, NULL) == -1)
821 bb_perror_msg_and_die("settimeofday"); 882 bb_perror_msg_and_die("settimeofday");
822 883
823 tval = tv.tv_sec; 884 VERB2 {
824 strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Z %Y", localtime(&tval)); 885 tval = tvc.tv_sec;
825 886 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&tval));
826 bb_error_msg("setting clock to %s (offset %fs)", buf, offset); 887 bb_error_msg("current time is %s.%06u", buf, (unsigned)tvc.tv_usec);
888 }
889 tval = tvn.tv_sec;
890 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&tval));
891 bb_error_msg("setting time to %s.%06u (offset %+fs)", buf, (unsigned)tvn.tv_usec, offset);
827 892
828 /* Correct various fields which contain time-relative values: */ 893 /* Correct various fields which contain time-relative values: */
829 894
@@ -831,7 +896,7 @@ step_time(double offset)
831 for (item = G.ntp_peers; item != NULL; item = item->link) { 896 for (item = G.ntp_peers; item != NULL; item = item->link) {
832 peer_t *pp = (peer_t *) item->data; 897 peer_t *pp = (peer_t *) item->data;
833 reset_peer_stats(pp, offset); 898 reset_peer_stats(pp, offset);
834 //bb_error_msg("offset:%f pp->next_action_time:%f -> %f", 899 //bb_error_msg("offset:%+f pp->next_action_time:%f -> %f",
835 // offset, pp->next_action_time, pp->next_action_time + offset); 900 // offset, pp->next_action_time, pp->next_action_time + offset);
836 pp->next_action_time += offset; 901 pp->next_action_time += offset;
837 } 902 }
@@ -1167,7 +1232,7 @@ select_and_cluster(void)
1167 } 1232 }
1168 G.last_update_peer = p; 1233 G.last_update_peer = p;
1169 keep_old: 1234 keep_old:
1170 VERB3 bb_error_msg("selected peer %s filter_offset:%f age:%f", 1235 VERB3 bb_error_msg("selected peer %s filter_offset:%+f age:%f",
1171 p->p_dotted, 1236 p->p_dotted,
1172 p->filter_offset, 1237 p->filter_offset,
1173 G.cur_time - p->lastpkt_recv_time 1238 G.cur_time - p->lastpkt_recv_time
@@ -1258,7 +1323,7 @@ update_local_clock(peer_t *p)
1258 switch (G.discipline_state) { 1323 switch (G.discipline_state) {
1259 case STATE_SYNC: 1324 case STATE_SYNC:
1260 /* The first outlyer: ignore it, switch to SPIK state */ 1325 /* The first outlyer: ignore it, switch to SPIK state */
1261 VERB3 bb_error_msg("offset:%f - spike detected", offset); 1326 VERB3 bb_error_msg("offset:%+f - spike detected", offset);
1262 G.discipline_state = STATE_SPIK; 1327 G.discipline_state = STATE_SPIK;
1263 return -1; /* "decrease poll interval" */ 1328 return -1; /* "decrease poll interval" */
1264 1329
@@ -1295,7 +1360,7 @@ update_local_clock(peer_t *p)
1295 * is always suppressed, even at the longer poll 1360 * is always suppressed, even at the longer poll
1296 * intervals. 1361 * intervals.
1297 */ 1362 */
1298 VERB3 bb_error_msg("stepping time by %f; poll_exp=MINPOLL", offset); 1363 VERB3 bb_error_msg("stepping time by %+f; poll_exp=MINPOLL", offset);
1299 step_time(offset); 1364 step_time(offset);
1300 if (option_mask32 & OPT_q) { 1365 if (option_mask32 & OPT_q) {
1301 /* We were only asked to set time once. Done. */ 1366 /* We were only asked to set time once. Done. */
@@ -1314,12 +1379,13 @@ update_local_clock(peer_t *p)
1314 return 1; /* "ok to increase poll interval" */ 1379 return 1; /* "ok to increase poll interval" */
1315 } 1380 }
1316#endif 1381#endif
1317 set_new_values(STATE_SYNC, /*offset:*/ 0, recv_time); 1382 abs_offset = offset = 0;
1383 set_new_values(STATE_SYNC, offset, recv_time);
1318 1384
1319 } else { /* abs_offset <= STEP_THRESHOLD */ 1385 } else { /* abs_offset <= STEP_THRESHOLD */
1320 1386
1321 if (G.poll_exp < MINPOLL && G.initial_poll_complete) { 1387 if (G.poll_exp < MINPOLL && G.initial_poll_complete) {
1322 VERB3 bb_error_msg("small offset:%f, disabling burst mode", offset); 1388 VERB3 bb_error_msg("small offset:%+f, disabling burst mode", offset);
1323 G.polladj_count = 0; 1389 G.polladj_count = 0;
1324 G.poll_exp = MINPOLL; 1390 G.poll_exp = MINPOLL;
1325 } 1391 }
@@ -1328,9 +1394,8 @@ update_local_clock(peer_t *p)
1328 * weighted offset differences. Used by the poll adjust code. 1394 * weighted offset differences. Used by the poll adjust code.
1329 */ 1395 */
1330 etemp = SQUARE(G.discipline_jitter); 1396 etemp = SQUARE(G.discipline_jitter);
1331 dtemp = SQUARE(MAXD(fabs(offset - G.last_update_offset), G_precision_sec)); 1397 dtemp = SQUARE(offset - G.last_update_offset);
1332 G.discipline_jitter = SQRT(etemp + (dtemp - etemp) / AVG); 1398 G.discipline_jitter = SQRT(etemp + (dtemp - etemp) / AVG);
1333 VERB3 bb_error_msg("discipline jitter=%f", G.discipline_jitter);
1334 1399
1335 switch (G.discipline_state) { 1400 switch (G.discipline_state) {
1336 case STATE_NSET: 1401 case STATE_NSET:
@@ -1407,6 +1472,10 @@ update_local_clock(peer_t *p)
1407 } 1472 }
1408 } 1473 }
1409 1474
1475 if (G.discipline_jitter < G_precision_sec)
1476 G.discipline_jitter = G_precision_sec;
1477 G.offset_to_jitter_ratio = abs_offset / G.discipline_jitter;
1478
1410 G.reftime = G.cur_time; 1479 G.reftime = G.cur_time;
1411 G.ntp_status = p->lastpkt_status; 1480 G.ntp_status = p->lastpkt_status;
1412 G.refid = p->lastpkt_refid; 1481 G.refid = p->lastpkt_refid;
@@ -1418,7 +1487,7 @@ update_local_clock(peer_t *p)
1418 1487
1419 /* We are in STATE_SYNC now, but did not do adjtimex yet. 1488 /* We are in STATE_SYNC now, but did not do adjtimex yet.
1420 * (Any other state does not reach this, they all return earlier) 1489 * (Any other state does not reach this, they all return earlier)
1421 * By this time, freq_drift and G.last_update_offset are set 1490 * By this time, freq_drift and offset are set
1422 * to values suitable for adjtimex. 1491 * to values suitable for adjtimex.
1423 */ 1492 */
1424#if !USING_KERNEL_PLL_LOOP 1493#if !USING_KERNEL_PLL_LOOP
@@ -1444,8 +1513,8 @@ update_local_clock(peer_t *p)
1444 memset(&tmx, 0, sizeof(tmx)); 1513 memset(&tmx, 0, sizeof(tmx));
1445 if (adjtimex(&tmx) < 0) 1514 if (adjtimex(&tmx) < 0)
1446 bb_perror_msg_and_die("adjtimex"); 1515 bb_perror_msg_and_die("adjtimex");
1447 VERB3 bb_error_msg("p adjtimex freq:%ld offset:%ld constant:%ld status:0x%x", 1516 bb_error_msg("p adjtimex freq:%ld offset:%+ld status:0x%x tc:%ld",
1448 tmx.freq, tmx.offset, tmx.constant, tmx.status); 1517 tmx.freq, tmx.offset, tmx.status, tmx.constant);
1449 } 1518 }
1450 1519
1451 memset(&tmx, 0, sizeof(tmx)); 1520 memset(&tmx, 0, sizeof(tmx));
@@ -1457,40 +1526,42 @@ update_local_clock(peer_t *p)
1457 tmx.modes = ADJ_FREQUENCY | ADJ_OFFSET; 1526 tmx.modes = ADJ_FREQUENCY | ADJ_OFFSET;
1458 /* 65536 is one ppm */ 1527 /* 65536 is one ppm */
1459 tmx.freq = G.discipline_freq_drift * 65536e6; 1528 tmx.freq = G.discipline_freq_drift * 65536e6;
1460 tmx.offset = G.last_update_offset * 1000000; /* usec */
1461#endif 1529#endif
1462 tmx.modes = ADJ_OFFSET | ADJ_STATUS | ADJ_TIMECONST;// | ADJ_MAXERROR | ADJ_ESTERROR; 1530 tmx.modes = ADJ_OFFSET | ADJ_STATUS | ADJ_TIMECONST;// | ADJ_MAXERROR | ADJ_ESTERROR;
1463 tmx.offset = (G.last_update_offset * 1000000); /* usec */ 1531 tmx.offset = (offset * 1000000); /* usec */
1464 /* + (G.last_update_offset < 0 ? -0.5 : 0.5) - too small to bother */
1465 tmx.status = STA_PLL; 1532 tmx.status = STA_PLL;
1466 if (G.ntp_status & LI_PLUSSEC) 1533 if (G.ntp_status & LI_PLUSSEC)
1467 tmx.status |= STA_INS; 1534 tmx.status |= STA_INS;
1468 if (G.ntp_status & LI_MINUSSEC) 1535 if (G.ntp_status & LI_MINUSSEC)
1469 tmx.status |= STA_DEL; 1536 tmx.status |= STA_DEL;
1537
1470 tmx.constant = G.poll_exp - 4; 1538 tmx.constant = G.poll_exp - 4;
1471 //tmx.esterror = (u_int32)(clock_jitter * 1e6); 1539 /* EXPERIMENTAL.
1472 //tmx.maxerror = (u_int32)((sys_rootdelay / 2 + sys_rootdisp) * 1e6); 1540 * The below if statement should be unnecessary, but...
1541 * It looks like Linux kernel's PLL is far too gentle in changing
1542 * tmx.freq in response to clock offset. Offset keeps growing
1543 * and eventually we fall back to smaller poll intervals.
1544 * We can make correction more agressive (about x2) by supplying
1545 * PLL time constant which is one less than the real one.
1546 * To be on a safe side, let's do it only if offset is significantly
1547 * larger than jitter.
1548 */
1549 if (tmx.constant > 0 && G.offset_to_jitter_ratio >= TIMECONST_HACK_GATE)
1550 tmx.constant--;
1551
1552 //tmx.esterror = (uint32_t)(clock_jitter * 1e6);
1553 //tmx.maxerror = (uint32_t)((sys_rootdelay / 2 + sys_rootdisp) * 1e6);
1473 rc = adjtimex(&tmx); 1554 rc = adjtimex(&tmx);
1474 if (rc < 0) 1555 if (rc < 0)
1475 bb_perror_msg_and_die("adjtimex"); 1556 bb_perror_msg_and_die("adjtimex");
1476 /* NB: here kernel returns constant == G.poll_exp, not == G.poll_exp - 4. 1557 /* NB: here kernel returns constant == G.poll_exp, not == G.poll_exp - 4.
1477 * Not sure why. Perhaps it is normal. 1558 * Not sure why. Perhaps it is normal.
1478 */ 1559 */
1479 VERB3 bb_error_msg("adjtimex:%d freq:%ld offset:%ld constant:%ld status:0x%x", 1560 VERB3 bb_error_msg("adjtimex:%d freq:%ld offset:%+ld status:0x%x",
1480 rc, tmx.freq, tmx.offset, tmx.constant, tmx.status); 1561 rc, tmx.freq, tmx.offset, tmx.status);
1481#if 0
1482 VERB3 {
1483 /* always gives the same output as above msg */
1484 memset(&tmx, 0, sizeof(tmx));
1485 if (adjtimex(&tmx) < 0)
1486 bb_perror_msg_and_die("adjtimex");
1487 VERB3 bb_error_msg("c adjtimex freq:%ld offset:%ld constant:%ld status:0x%x",
1488 tmx.freq, tmx.offset, tmx.constant, tmx.status);
1489 }
1490#endif
1491 G.kernel_freq_drift = tmx.freq / 65536; 1562 G.kernel_freq_drift = tmx.freq / 65536;
1492 VERB2 bb_error_msg("update peer:%s, offset:%f, clock drift:%ld ppm", 1563 VERB2 bb_error_msg("update from:%s offset:%+f jitter:%f clock drift:%+.3fppm tc:%d",
1493 p->p_dotted, G.last_update_offset, G.kernel_freq_drift); 1564 p->p_dotted, offset, G.discipline_jitter, (double)tmx.freq / 65536, (int)tmx.constant);
1494 1565
1495 return 1; /* "ok to increase poll interval" */ 1566 return 1; /* "ok to increase poll interval" */
1496} 1567}
@@ -1626,22 +1697,22 @@ recv_and_process_peer_pkt(peer_t *p)
1626 if (!p->reachable_bits) { 1697 if (!p->reachable_bits) {
1627 /* 1st datapoint ever - replicate offset in every element */ 1698 /* 1st datapoint ever - replicate offset in every element */
1628 int i; 1699 int i;
1629 for (i = 1; i < NUM_DATAPOINTS; i++) { 1700 for (i = 0; i < NUM_DATAPOINTS; i++) {
1630 p->filter_datapoint[i].d_offset = datapoint->d_offset; 1701 p->filter_datapoint[i].d_offset = datapoint->d_offset;
1631 } 1702 }
1632 } 1703 }
1633 1704
1634 p->reachable_bits |= 1; 1705 p->reachable_bits |= 1;
1635 if ((MAX_VERBOSE && G.verbose) || (option_mask32 & OPT_w)) { 1706 if ((MAX_VERBOSE && G.verbose) || (option_mask32 & OPT_w)) {
1636 bb_error_msg("reply from %s: reach 0x%02x offset %f delay %f status 0x%02x strat %d refid 0x%08x rootdelay %f", 1707 bb_error_msg("reply from %s: offset:%+f delay:%f status:0x%02x strat:%d refid:0x%08x rootdelay:%f reach:0x%02x",
1637 p->p_dotted, 1708 p->p_dotted,
1638 p->reachable_bits,
1639 datapoint->d_offset, 1709 datapoint->d_offset,
1640 p->lastpkt_delay, 1710 p->lastpkt_delay,
1641 p->lastpkt_status, 1711 p->lastpkt_status,
1642 p->lastpkt_stratum, 1712 p->lastpkt_stratum,
1643 p->lastpkt_refid, 1713 p->lastpkt_refid,
1644 p->lastpkt_rootdelay 1714 p->lastpkt_rootdelay,
1715 p->reachable_bits
1645 /* not shown: m_ppoll, m_precision_exp, m_rootdisp, 1716 /* not shown: m_ppoll, m_precision_exp, m_rootdisp,
1646 * m_reftime, m_orgtime, m_rectime, m_xmttime 1717 * m_reftime, m_orgtime, m_rectime, m_xmttime
1647 */ 1718 */
@@ -1660,7 +1731,7 @@ recv_and_process_peer_pkt(peer_t *p)
1660 * drop poll interval one step down. 1731 * drop poll interval one step down.
1661 */ 1732 */
1662 if (fabs(q->filter_offset) >= POLLDOWN_OFFSET) { 1733 if (fabs(q->filter_offset) >= POLLDOWN_OFFSET) {
1663 VERB3 bb_error_msg("offset:%f > POLLDOWN_OFFSET", q->filter_offset); 1734 VERB3 bb_error_msg("offset:%+f > POLLDOWN_OFFSET", q->filter_offset);
1664 goto poll_down; 1735 goto poll_down;
1665 } 1736 }
1666 } 1737 }
@@ -1674,14 +1745,7 @@ recv_and_process_peer_pkt(peer_t *p)
1674 * is increased, otherwise it is decreased. A bit of hysteresis 1745 * is increased, otherwise it is decreased. A bit of hysteresis
1675 * helps calm the dance. Works best using burst mode. 1746 * helps calm the dance. Works best using burst mode.
1676 */ 1747 */
1677 VERB4 if (rc > 0) { 1748 if (rc > 0 && G.offset_to_jitter_ratio <= POLLADJ_GATE) {
1678 bb_error_msg("offset:%f POLLADJ_GATE*discipline_jitter:%f poll:%s",
1679 q->filter_offset, POLLADJ_GATE * G.discipline_jitter,
1680 fabs(q->filter_offset) < POLLADJ_GATE * G.discipline_jitter
1681 ? "grows" : "falls"
1682 );
1683 }
1684 if (rc > 0 && fabs(q->filter_offset) < POLLADJ_GATE * G.discipline_jitter) {
1685 /* was += G.poll_exp but it is a bit 1749 /* was += G.poll_exp but it is a bit
1686 * too optimistic for my taste at high poll_exp's */ 1750 * too optimistic for my taste at high poll_exp's */
1687 G.polladj_count += MINPOLL; 1751 G.polladj_count += MINPOLL;
@@ -2060,8 +2124,23 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv)
2060 timeout++; /* (nextaction - G.cur_time) rounds down, compensating */ 2124 timeout++; /* (nextaction - G.cur_time) rounds down, compensating */
2061 2125
2062 /* Here we may block */ 2126 /* Here we may block */
2063 VERB2 bb_error_msg("poll %us, sockets:%u, poll interval:%us", timeout, i, 1 << G.poll_exp); 2127 VERB2 {
2128 if (i > (ENABLE_FEATURE_NTPD_SERVER && G.listen_fd != -1)) {
2129 /* We wait for at least one reply.
2130 * Poll for it, without wasting time for message.
2131 * Since replies often come under 1 second, this also
2132 * reduces clutter in logs.
2133 */
2134 nfds = poll(pfd, i, 1000);
2135 if (nfds != 0)
2136 goto did_poll;
2137 if (--timeout <= 0)
2138 goto did_poll;
2139 }
2140 bb_error_msg("poll:%us sockets:%u interval:%us", timeout, i, 1 << G.poll_exp);
2141 }
2064 nfds = poll(pfd, i, timeout * 1000); 2142 nfds = poll(pfd, i, timeout * 1000);
2143 did_poll:
2065 gettime1900d(); /* sets G.cur_time */ 2144 gettime1900d(); /* sets G.cur_time */
2066 if (nfds <= 0) { 2145 if (nfds <= 0) {
2067 if (G.script_name && G.cur_time - G.last_script_run > 11*60) { 2146 if (G.script_name && G.cur_time - G.last_script_run > 11*60) {
diff --git a/networking/tftp.c b/networking/tftp.c
index 043b879af..ce48a1edd 100644
--- a/networking/tftp.c
+++ b/networking/tftp.c
@@ -789,8 +789,9 @@ int tftpd_main(int argc UNUSED_PARAM, char **argv)
789 openlog(applet_name, LOG_PID, LOG_DAEMON); 789 openlog(applet_name, LOG_PID, LOG_DAEMON);
790 logmode = LOGMODE_SYSLOG; 790 logmode = LOGMODE_SYSLOG;
791 } 791 }
792 if (argv[0]) 792 if (argv[0]) {
793 xchdir(argv[0]); 793 xchroot(argv[0]);
794 }
794 795
795 result = recv_from_to(STDIN_FILENO, block_buf, sizeof(block_buf), 796 result = recv_from_to(STDIN_FILENO, block_buf, sizeof(block_buf),
796 0 /* flags */, 797 0 /* flags */,
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index 2e6113627..ae0e0d306 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -29,9 +29,9 @@ const struct dhcp_optflag dhcp_optflags[] = {
29// { OPTION_IP | OPTION_LIST , 0x07 }, /* DHCP_LOG_SERVER */ 29// { OPTION_IP | OPTION_LIST , 0x07 }, /* DHCP_LOG_SERVER */
30// { OPTION_IP | OPTION_LIST , 0x08 }, /* DHCP_COOKIE_SERVER */ 30// { OPTION_IP | OPTION_LIST , 0x08 }, /* DHCP_COOKIE_SERVER */
31 { OPTION_IP | OPTION_LIST , 0x09 }, /* DHCP_LPR_SERVER */ 31 { OPTION_IP | OPTION_LIST , 0x09 }, /* DHCP_LPR_SERVER */
32 { OPTION_STRING | OPTION_REQ, 0x0c }, /* DHCP_HOST_NAME */ 32 { OPTION_STRING_HOST | OPTION_REQ, 0x0c }, /* DHCP_HOST_NAME */
33 { OPTION_U16 , 0x0d }, /* DHCP_BOOT_SIZE */ 33 { OPTION_U16 , 0x0d }, /* DHCP_BOOT_SIZE */
34 { OPTION_STRING | OPTION_REQ, 0x0f }, /* DHCP_DOMAIN_NAME */ 34 { OPTION_STRING_HOST | OPTION_REQ, 0x0f }, /* DHCP_DOMAIN_NAME */
35 { OPTION_IP , 0x10 }, /* DHCP_SWAP_SERVER */ 35 { OPTION_IP , 0x10 }, /* DHCP_SWAP_SERVER */
36 { OPTION_STRING , 0x11 }, /* DHCP_ROOT_PATH */ 36 { OPTION_STRING , 0x11 }, /* DHCP_ROOT_PATH */
37 { OPTION_U8 , 0x17 }, /* DHCP_IP_TTL */ 37 { OPTION_U8 , 0x17 }, /* DHCP_IP_TTL */
@@ -41,7 +41,7 @@ const struct dhcp_optflag dhcp_optflags[] = {
41//server would let us know anyway? 41//server would let us know anyway?
42 { OPTION_IP | OPTION_REQ, 0x1c }, /* DHCP_BROADCAST */ 42 { OPTION_IP | OPTION_REQ, 0x1c }, /* DHCP_BROADCAST */
43 { OPTION_IP_PAIR | OPTION_LIST , 0x21 }, /* DHCP_ROUTES */ 43 { OPTION_IP_PAIR | OPTION_LIST , 0x21 }, /* DHCP_ROUTES */
44 { OPTION_STRING , 0x28 }, /* DHCP_NIS_DOMAIN */ 44 { OPTION_STRING_HOST , 0x28 }, /* DHCP_NIS_DOMAIN */
45 { OPTION_IP | OPTION_LIST , 0x29 }, /* DHCP_NIS_SERVER */ 45 { OPTION_IP | OPTION_LIST , 0x29 }, /* DHCP_NIS_SERVER */
46 { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a }, /* DHCP_NTP_SERVER */ 46 { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a }, /* DHCP_NTP_SERVER */
47 { OPTION_IP | OPTION_LIST , 0x2c }, /* DHCP_WINS_SERVER */ 47 { OPTION_IP | OPTION_LIST , 0x2c }, /* DHCP_WINS_SERVER */
@@ -49,7 +49,7 @@ const struct dhcp_optflag dhcp_optflags[] = {
49 { OPTION_IP , 0x36 }, /* DHCP_SERVER_ID */ 49 { OPTION_IP , 0x36 }, /* DHCP_SERVER_ID */
50 { OPTION_STRING , 0x38 }, /* DHCP_ERR_MESSAGE */ 50 { OPTION_STRING , 0x38 }, /* DHCP_ERR_MESSAGE */
51//TODO: must be combined with 'sname' and 'file' handling: 51//TODO: must be combined with 'sname' and 'file' handling:
52 { OPTION_STRING , 0x42 }, /* DHCP_TFTP_SERVER_NAME */ 52 { OPTION_STRING_HOST , 0x42 }, /* DHCP_TFTP_SERVER_NAME */
53 { OPTION_STRING , 0x43 }, /* DHCP_BOOT_FILE */ 53 { OPTION_STRING , 0x43 }, /* DHCP_BOOT_FILE */
54//TODO: not a string, but a set of LASCII strings: 54//TODO: not a string, but a set of LASCII strings:
55// { OPTION_STRING , 0x4D }, /* DHCP_USER_CLASS */ 55// { OPTION_STRING , 0x4D }, /* DHCP_USER_CLASS */
@@ -57,13 +57,13 @@ const struct dhcp_optflag dhcp_optflags[] = {
57 { OPTION_DNS_STRING | OPTION_LIST , 0x77 }, /* DHCP_DOMAIN_SEARCH */ 57 { OPTION_DNS_STRING | OPTION_LIST , 0x77 }, /* DHCP_DOMAIN_SEARCH */
58 { OPTION_SIP_SERVERS , 0x78 }, /* DHCP_SIP_SERVERS */ 58 { OPTION_SIP_SERVERS , 0x78 }, /* DHCP_SIP_SERVERS */
59#endif 59#endif
60 { OPTION_STATIC_ROUTES , 0x79 }, /* DHCP_STATIC_ROUTES */ 60 { OPTION_STATIC_ROUTES | OPTION_LIST , 0x79 }, /* DHCP_STATIC_ROUTES */
61#if ENABLE_FEATURE_UDHCP_8021Q 61#if ENABLE_FEATURE_UDHCP_8021Q
62 { OPTION_U16 , 0x84 }, /* DHCP_VLAN_ID */ 62 { OPTION_U16 , 0x84 }, /* DHCP_VLAN_ID */
63 { OPTION_U8 , 0x85 }, /* DHCP_VLAN_PRIORITY */ 63 { OPTION_U8 , 0x85 }, /* DHCP_VLAN_PRIORITY */
64#endif 64#endif
65 { OPTION_6RD , 0xd4 }, /* DHCP_6RD */ 65 { OPTION_6RD , 0xd4 }, /* DHCP_6RD */
66 { OPTION_STATIC_ROUTES , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */ 66 { OPTION_STATIC_ROUTES | OPTION_LIST , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */
67 { OPTION_STRING , 0xfc }, /* DHCP_WPAD */ 67 { OPTION_STRING , 0xfc }, /* DHCP_WPAD */
68 68
69 /* Options below have no match in dhcp_option_strings[], 69 /* Options below have no match in dhcp_option_strings[],
@@ -123,8 +123,6 @@ const char dhcp_option_strings[] ALIGN1 =
123// is not handled yet by "string->option" conversion code: 123// is not handled yet by "string->option" conversion code:
124 "sipsrv" "\0" /* DHCP_SIP_SERVERS */ 124 "sipsrv" "\0" /* DHCP_SIP_SERVERS */
125#endif 125#endif
126// doesn't work in udhcpd.conf since OPTION_STATIC_ROUTES
127// is not handled yet by "string->option" conversion code:
128 "staticroutes" "\0"/* DHCP_STATIC_ROUTES */ 126 "staticroutes" "\0"/* DHCP_STATIC_ROUTES */
129#if ENABLE_FEATURE_UDHCP_8021Q 127#if ENABLE_FEATURE_UDHCP_8021Q
130 "vlanid" "\0" /* DHCP_VLAN_ID */ 128 "vlanid" "\0" /* DHCP_VLAN_ID */
@@ -148,6 +146,7 @@ const uint8_t dhcp_option_lengths[] ALIGN1 = {
148 [OPTION_IP_PAIR] = 8, 146 [OPTION_IP_PAIR] = 8,
149// [OPTION_BOOLEAN] = 1, 147// [OPTION_BOOLEAN] = 1,
150 [OPTION_STRING] = 1, /* ignored by udhcp_str2optset */ 148 [OPTION_STRING] = 1, /* ignored by udhcp_str2optset */
149 [OPTION_STRING_HOST] = 1, /* ignored by udhcp_str2optset */
151#if ENABLE_FEATURE_UDHCP_RFC3397 150#if ENABLE_FEATURE_UDHCP_RFC3397
152 [OPTION_DNS_STRING] = 1, /* ignored by both udhcp_str2optset and xmalloc_optname_optval */ 151 [OPTION_DNS_STRING] = 1, /* ignored by both udhcp_str2optset and xmalloc_optname_optval */
153 [OPTION_SIP_SERVERS] = 1, 152 [OPTION_SIP_SERVERS] = 1,
@@ -337,7 +336,8 @@ int FAST_FUNC udhcp_str2nip(const char *str, void *arg)
337 lsa = host_and_af2sockaddr(str, 0, AF_INET); 336 lsa = host_and_af2sockaddr(str, 0, AF_INET);
338 if (!lsa) 337 if (!lsa)
339 return 0; 338 return 0;
340 *(uint32_t*)arg = lsa->u.sin.sin_addr.s_addr; 339 /* arg maybe unaligned */
340 move_to_unaligned32((uint32_t*)arg, lsa->u.sin.sin_addr.s_addr);
341 free(lsa); 341 free(lsa);
342 return 1; 342 return 1;
343} 343}
@@ -417,7 +417,9 @@ static NOINLINE void attach_option(
417 /* actually 255 is ok too, but adding a space can overlow it */ 417 /* actually 255 is ok too, but adding a space can overlow it */
418 418
419 existing->data = xrealloc(existing->data, OPT_DATA + 1 + old_len + length); 419 existing->data = xrealloc(existing->data, OPT_DATA + 1 + old_len + length);
420 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING) { 420 if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING
421 || (optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING_HOST
422 ) {
421 /* add space separator between STRING options in a list */ 423 /* add space separator between STRING options in a list */
422 existing->data[OPT_DATA + old_len] = ' '; 424 existing->data[OPT_DATA + old_len] = ' ';
423 old_len++; 425 old_len++;
@@ -434,13 +436,14 @@ static NOINLINE void attach_option(
434int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg) 436int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg)
435{ 437{
436 struct option_set **opt_list = arg; 438 struct option_set **opt_list = arg;
437 char *opt, *val, *endptr; 439 char *opt, *val;
438 char *str; 440 char *str;
439 const struct dhcp_optflag *optflag; 441 const struct dhcp_optflag *optflag;
440 struct dhcp_optflag bin_optflag; 442 struct dhcp_optflag bin_optflag;
441 unsigned optcode; 443 unsigned optcode;
442 int retval, length; 444 int retval, length;
443 char buffer[8] ALIGNED(4); 445 /* IP_PAIR needs 8 bytes, STATIC_ROUTES needs 9 max */
446 char buffer[9] ALIGNED(4);
444 uint16_t *result_u16 = (uint16_t *) buffer; 447 uint16_t *result_u16 = (uint16_t *) buffer;
445 uint32_t *result_u32 = (uint32_t *) buffer; 448 uint32_t *result_u32 = (uint32_t *) buffer;
446 449
@@ -481,6 +484,7 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg)
481 retval = udhcp_str2nip(val, buffer + 4); 484 retval = udhcp_str2nip(val, buffer + 4);
482 break; 485 break;
483 case OPTION_STRING: 486 case OPTION_STRING:
487 case OPTION_STRING_HOST:
484#if ENABLE_FEATURE_UDHCP_RFC3397 488#if ENABLE_FEATURE_UDHCP_RFC3397
485 case OPTION_DNS_STRING: 489 case OPTION_DNS_STRING:
486#endif 490#endif
@@ -497,34 +501,53 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg)
497// break; 501// break;
498// } 502// }
499 case OPTION_U8: 503 case OPTION_U8:
500 buffer[0] = strtoul(val, &endptr, 0); 504 buffer[0] = bb_strtou32(val, NULL, 0);
501 retval = (endptr[0] == '\0'); 505 retval = (errno == 0);
502 break; 506 break;
503 /* htonX are macros in older libc's, using temp var 507 /* htonX are macros in older libc's, using temp var
504 * in code below for safety */ 508 * in code below for safety */
505 /* TODO: use bb_strtoX? */ 509 /* TODO: use bb_strtoX? */
506 case OPTION_U16: { 510 case OPTION_U16: {
507 unsigned long tmp = strtoul(val, &endptr, 0); 511 uint32_t tmp = bb_strtou32(val, NULL, 0);
508 *result_u16 = htons(tmp); 512 *result_u16 = htons(tmp);
509 retval = (endptr[0] == '\0' /*&& tmp < 0x10000*/); 513 retval = (errno == 0 /*&& tmp < 0x10000*/);
510 break; 514 break;
511 } 515 }
512// case OPTION_S16: { 516// case OPTION_S16: {
513// long tmp = strtol(val, &endptr, 0); 517// long tmp = bb_strtoi32(val, NULL, 0);
514// *result_u16 = htons(tmp); 518// *result_u16 = htons(tmp);
515// retval = (endptr[0] == '\0'); 519// retval = (errno == 0);
516// break; 520// break;
517// } 521// }
518 case OPTION_U32: { 522 case OPTION_U32: {
519 unsigned long tmp = strtoul(val, &endptr, 0); 523 uint32_t tmp = bb_strtou32(val, NULL, 0);
520 *result_u32 = htonl(tmp); 524 *result_u32 = htonl(tmp);
521 retval = (endptr[0] == '\0'); 525 retval = (errno == 0);
522 break; 526 break;
523 } 527 }
524 case OPTION_S32: { 528 case OPTION_S32: {
525 long tmp = strtol(val, &endptr, 0); 529 int32_t tmp = bb_strtoi32(val, NULL, 0);
526 *result_u32 = htonl(tmp); 530 *result_u32 = htonl(tmp);
527 retval = (endptr[0] == '\0'); 531 retval = (errno == 0);
532 break;
533 }
534 case OPTION_STATIC_ROUTES: {
535 /* Input: "a.b.c.d/m" */
536 /* Output: mask(1 byte),pfx(0-4 bytes),gw(4 bytes) */
537 unsigned mask;
538 char *slash = strchr(val, '/');
539 if (slash) {
540 *slash = '\0';
541 retval = udhcp_str2nip(val, buffer + 1);
542 buffer[0] = mask = bb_strtou(slash + 1, NULL, 10);
543 val = strtok(NULL, ", \t/-");
544 if (!val || mask > 32 || errno)
545 retval = 0;
546 if (retval) {
547 length = ((mask + 7) >> 3) + 5;
548 retval = udhcp_str2nip(val, buffer + (length - 4));
549 }
550 }
528 break; 551 break;
529 } 552 }
530 case OPTION_BIN: /* handled in attach_option() */ 553 case OPTION_BIN: /* handled in attach_option() */
@@ -535,7 +558,26 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg)
535 } 558 }
536 if (retval) 559 if (retval)
537 attach_option(opt_list, optflag, opt, length); 560 attach_option(opt_list, optflag, opt, length);
538 } while (retval && optflag->flags & OPTION_LIST); 561 } while (retval && (optflag->flags & OPTION_LIST));
539 562
540 return retval; 563 return retval;
541} 564}
565
566/* note: ip is a pointer to an IPv6 in network order, possibly misaliged */
567int FAST_FUNC sprint_nip6(char *dest, /*const char *pre,*/ const uint8_t *ip)
568{
569 char hexstrbuf[16 * 2];
570 bin2hex(hexstrbuf, (void*)ip, 16);
571 return sprintf(dest, /* "%s" */
572 "%.4s:%.4s:%.4s:%.4s:%.4s:%.4s:%.4s:%.4s",
573 /* pre, */
574 hexstrbuf + 0 * 4,
575 hexstrbuf + 1 * 4,
576 hexstrbuf + 2 * 4,
577 hexstrbuf + 3 * 4,
578 hexstrbuf + 4 * 4,
579 hexstrbuf + 5 * 4,
580 hexstrbuf + 6 * 4,
581 hexstrbuf + 7 * 4
582 );
583}
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
index a7f9395b8..cfd58679a 100644
--- a/networking/udhcp/common.h
+++ b/networking/udhcp/common.h
@@ -80,6 +80,9 @@ enum {
80 OPTION_IP = 1, 80 OPTION_IP = 1,
81 OPTION_IP_PAIR, 81 OPTION_IP_PAIR,
82 OPTION_STRING, 82 OPTION_STRING,
83 /* Opts of STRING_HOST type will be sanitized before they are passed
84 * to udhcpc script's environment: */
85 OPTION_STRING_HOST,
83// OPTION_BOOLEAN, 86// OPTION_BOOLEAN,
84 OPTION_U8, 87 OPTION_U8,
85 OPTION_U16, 88 OPTION_U16,
@@ -308,6 +311,9 @@ int arpping(uint32_t test_nip,
308 uint8_t *from_mac, 311 uint8_t *from_mac,
309 const char *interface) FAST_FUNC; 312 const char *interface) FAST_FUNC;
310 313
314/* note: ip is a pointer to an IPv6 in network order, possibly misaliged */
315int sprint_nip6(char *dest, /*const char *pre,*/ const uint8_t *ip) FAST_FUNC;
316
311POP_SAVED_FUNCTION_VISIBILITY 317POP_SAVED_FUNCTION_VISIBILITY
312 318
313#endif 319#endif
diff --git a/networking/udhcp/d6_common.h b/networking/udhcp/d6_common.h
new file mode 100644
index 000000000..4dd7e621e
--- /dev/null
+++ b/networking/udhcp/d6_common.h
@@ -0,0 +1,123 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 2011 Denys Vlasenko.
4 *
5 * Licensed under GPLv2, see file LICENSE in this source tree.
6 */
7#ifndef UDHCP_D6_COMMON_H
8#define UDHCP_D6_COMMON_H 1
9
10#include <netinet/ip6.h>
11
12PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
13
14
15/*** DHCPv6 packet ***/
16
17/* DHCPv6 protocol. See RFC 3315 */
18#define D6_MSG_SOLICIT 1
19#define D6_MSG_ADVERTISE 2
20#define D6_MSG_REQUEST 3
21#define D6_MSG_CONFIRM 4
22#define D6_MSG_RENEW 5
23#define D6_MSG_REBIND 6
24#define D6_MSG_REPLY 7
25#define D6_MSG_RELEASE 8
26#define D6_MSG_DECLINE 9
27#define D6_MSG_RECONFIGURE 10
28#define D6_MSG_INFORMATION_REQUEST 11
29#define D6_MSG_RELAY_FORW 12
30#define D6_MSG_RELAY_REPL 13
31
32struct d6_packet {
33 union {
34 uint8_t d6_msg_type;
35 uint32_t d6_xid32;
36 } d6_u;
37 uint8_t d6_options[576 - sizeof(struct iphdr) - sizeof(struct udphdr) - 4
38 + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS];
39} PACKED;
40#define d6_msg_type d6_u.d6_msg_type
41#define d6_xid32 d6_u.d6_xid32
42
43struct ip6_udp_d6_packet {
44 struct ip6_hdr ip6;
45 struct udphdr udp;
46 struct d6_packet data;
47} PACKED;
48
49struct udp_d6_packet {
50 struct udphdr udp;
51 struct d6_packet data;
52} PACKED;
53
54/*** Options ***/
55
56struct d6_option {
57 uint8_t code_hi;
58 uint8_t code;
59 uint8_t len_hi;
60 uint8_t len;
61 uint8_t data[1];
62} PACKED;
63
64#define D6_OPT_CLIENTID 1
65#define D6_OPT_SERVERID 2
66#define D6_OPT_IA_NA 3
67#define D6_OPT_IA_TA 4
68#define D6_OPT_IAADDR 5
69#define D6_OPT_ORO 6
70#define D6_OPT_PREFERENCE 7
71#define D6_OPT_ELAPSED_TIME 8
72#define D6_OPT_RELAY_MSG 9
73#define D6_OPT_AUTH 11
74#define D6_OPT_UNICAST 12
75#define D6_OPT_STATUS_CODE 13
76#define D6_OPT_RAPID_COMMIT 14
77#define D6_OPT_USER_CLASS 15
78#define D6_OPT_VENDOR_CLASS 16
79#define D6_OPT_VENDOR_OPTS 17
80#define D6_OPT_INTERFACE_ID 18
81#define D6_OPT_RECONF_MSG 19
82#define D6_OPT_RECONF_ACCEPT 20
83
84#define D6_OPT_IA_PD 25
85#define D6_OPT_IAPREFIX 26
86
87/*** Other shared functions ***/
88
89struct client6_data_t {
90 struct d6_option *server_id;
91 struct d6_option *ia_na;
92 char **env_ptr;
93 unsigned env_idx;
94};
95
96#define client6_data (*(struct client6_data_t*)(&bb_common_bufsiz1[COMMON_BUFSIZE - sizeof(struct client6_data_t)]))
97
98int FAST_FUNC d6_listen_socket(int port, const char *inf);
99
100int FAST_FUNC d6_recv_kernel_packet(
101 struct in6_addr *peer_ipv6,
102 struct d6_packet *packet, int fd
103);
104
105int FAST_FUNC d6_send_raw_packet(
106 struct d6_packet *d6_pkt, unsigned d6_pkt_size,
107 struct in6_addr *src_ipv6, int source_port,
108 struct in6_addr *dst_ipv6, int dest_port, const uint8_t *dest_arp,
109 int ifindex
110);
111
112int FAST_FUNC d6_send_kernel_packet(
113 struct d6_packet *d6_pkt, unsigned d6_pkt_size,
114 struct in6_addr *src_ipv6, int source_port,
115 struct in6_addr *dst_ipv6, int dest_port
116);
117
118void FAST_FUNC d6_dump_packet(struct d6_packet *packet);
119
120
121POP_SAVED_FUNCTION_VISIBILITY
122
123#endif
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
new file mode 100644
index 000000000..23e6862dc
--- /dev/null
+++ b/networking/udhcp/d6_dhcpc.c
@@ -0,0 +1,1483 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * DHCPv6 client.
4 *
5 * 2011-11.
6 * WARNING: THIS CODE IS INCOMPLETE. IT IS NOWHERE NEAR
7 * TO BE READY FOR PRODUCTION USE.
8 *
9 * Copyright (C) 2011 Denys Vlasenko.
10 *
11 * Licensed under GPLv2, see file LICENSE in this source tree.
12 */
13
14//config:config UDHCPC6
15//config: bool "udhcp client for DHCPv6 (udhcpc6)"
16//config: default n # not yet ready
17//config: help
18//config: udhcpc6 is a DHCPv6 client
19
20//applet:IF_UDHCPC6(APPLET(udhcpc6, BB_DIR_USR_BIN, BB_SUID_DROP))
21
22//kbuild:lib-$(CONFIG_UDHCPC6) += d6_dhcpc.o d6_packet.o d6_socket.o common.o
23
24
25#include <syslog.h>
26/* Override ENABLE_FEATURE_PIDFILE - ifupdown needs our pidfile to always exist */
27#define WANT_PIDFILE 1
28#include "common.h"
29#include "dhcpd.h"
30#include "dhcpc.h"
31#include "d6_common.h"
32
33#include <netinet/if_ether.h>
34#include <netpacket/packet.h>
35#include <linux/filter.h>
36
37/* "struct client_config_t client_config" is in bb_common_bufsiz1 */
38
39
40#if ENABLE_LONG_OPTS
41static const char udhcpc6_longopts[] ALIGN1 =
42 "interface\0" Required_argument "i"
43 "now\0" No_argument "n"
44 "pidfile\0" Required_argument "p"
45 "quit\0" No_argument "q"
46 "release\0" No_argument "R"
47 "request\0" Required_argument "r"
48 "script\0" Required_argument "s"
49 "timeout\0" Required_argument "T"
50 "retries\0" Required_argument "t"
51 "tryagain\0" Required_argument "A"
52 "syslog\0" No_argument "S"
53 "request-option\0" Required_argument "O"
54 "no-default-options\0" No_argument "o"
55 "foreground\0" No_argument "f"
56 "background\0" No_argument "b"
57/// IF_FEATURE_UDHCPC_ARPING("arping\0" No_argument "a")
58 IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P")
59 ;
60#endif
61/* Must match getopt32 option string order */
62enum {
63 OPT_i = 1 << 0,
64 OPT_n = 1 << 1,
65 OPT_p = 1 << 2,
66 OPT_q = 1 << 3,
67 OPT_R = 1 << 4,
68 OPT_r = 1 << 5,
69 OPT_s = 1 << 6,
70 OPT_T = 1 << 7,
71 OPT_t = 1 << 8,
72 OPT_S = 1 << 9,
73 OPT_A = 1 << 10,
74 OPT_O = 1 << 11,
75 OPT_o = 1 << 12,
76 OPT_x = 1 << 13,
77 OPT_f = 1 << 14,
78/* The rest has variable bit positions, need to be clever */
79 OPTBIT_f = 14,
80 USE_FOR_MMU( OPTBIT_b,)
81 ///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
82 IF_FEATURE_UDHCP_PORT( OPTBIT_P,)
83 USE_FOR_MMU( OPT_b = 1 << OPTBIT_b,)
84 ///IF_FEATURE_UDHCPC_ARPING(OPT_a = 1 << OPTBIT_a,)
85 IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,)
86};
87
88
89/*** Utility functions ***/
90
91static void *d6_find_option(uint8_t *option, uint8_t *option_end, unsigned code)
92{
93 /* "length minus 4" */
94 int len_m4 = option_end - option - 4;
95 while (len_m4 >= 0) {
96 /* Next option's len is too big? */
97 if (option[3] > len_m4)
98 return NULL; /* yes. bogus packet! */
99 /* So far we treat any opts with code >255
100 * or len >255 as bogus, and stop at once.
101 * This simplifies big-endian handling.
102 */
103 if (option[0] != 0 || option[2] != 0)
104 return NULL;
105 /* Option seems to be valid */
106 /* Does its code match? */
107 if (option[1] == code)
108 return option; /* yes! */
109 option += option[3] + 4;
110 len_m4 -= option[3] + 4;
111 }
112 return NULL;
113}
114
115static void *d6_copy_option(uint8_t *option, uint8_t *option_end, unsigned code)
116{
117 uint8_t *opt = d6_find_option(option, option_end, code);
118 if (!opt)
119 return opt;
120 return memcpy(xmalloc(opt[3] + 4), opt, opt[3] + 4);
121}
122
123static void *d6_store_blob(void *dst, const void *src, unsigned len)
124{
125 memcpy(dst, src, len);
126 return dst + len;
127}
128
129
130/*** Script execution code ***/
131
132static char** new_env(void)
133{
134 client6_data.env_ptr = xrealloc_vector(client6_data.env_ptr, 3, client6_data.env_idx);
135 return &client6_data.env_ptr[client6_data.env_idx++];
136}
137
138/* put all the parameters into the environment */
139static void option_to_env(uint8_t *option, uint8_t *option_end)
140{
141 /* "length minus 4" */
142 int len_m4 = option_end - option - 4;
143 while (len_m4 >= 0) {
144 uint32_t v32;
145 char ipv6str[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")];
146
147 if (option[0] != 0 || option[2] != 0)
148 break;
149
150 switch (option[1]) {
151 //case D6_OPT_CLIENTID:
152 //case D6_OPT_SERVERID:
153 case D6_OPT_IA_NA:
154 case D6_OPT_IA_PD:
155 option_to_env(option + 16, option + 4 + option[3]);
156 break;
157 //case D6_OPT_IA_TA:
158 case D6_OPT_IAADDR:
159/* 0 1 2 3
160 * 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
161 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
162 * | OPTION_IAADDR | option-len |
163 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
164 * | |
165 * | IPv6 address |
166 * | |
167 * | |
168 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
169 * | preferred-lifetime |
170 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
171 * | valid-lifetime |
172 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
173 */
174 sprint_nip6(ipv6str, option + 4);
175 *new_env() = xasprintf("ipv6=%s", ipv6str);
176
177 move_from_unaligned32(v32, option + 4 + 16 + 4);
178 *new_env() = xasprintf("lease=%u", (unsigned)v32);
179 break;
180
181 //case D6_OPT_ORO:
182 //case D6_OPT_PREFERENCE:
183 //case D6_OPT_ELAPSED_TIME:
184 //case D6_OPT_RELAY_MSG:
185 //case D6_OPT_AUTH:
186 //case D6_OPT_UNICAST:
187 //case D6_OPT_STATUS_CODE:
188 //case D6_OPT_RAPID_COMMIT:
189 //case D6_OPT_USER_CLASS:
190 //case D6_OPT_VENDOR_CLASS:
191 //case D6_OPT_VENDOR_OPTS:
192 //case D6_OPT_INTERFACE_ID:
193 //case D6_OPT_RECONF_MSG:
194 //case D6_OPT_RECONF_ACCEPT:
195
196 case D6_OPT_IAPREFIX:
197/* 0 1 2 3
198 * 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
199 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
200 * | OPTION_IAPREFIX | option-length |
201 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
202 * | preferred-lifetime |
203 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
204 * | valid-lifetime |
205 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
206 * | prefix-length | |
207 * +-+-+-+-+-+-+-+-+ IPv6 prefix |
208 * | (16 octets) |
209 * | |
210 * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
211 * | |
212 * +-+-+-+-+-+-+-+-+
213 */
214 //move_from_unaligned32(v32, option + 4 + 4);
215 //*new_env() = xasprintf("lease=%u", (unsigned)v32);
216
217 sprint_nip6(ipv6str, option + 4 + 4 + 1);
218 *new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4]));
219 }
220 option += 4 + option[3];
221 len_m4 -= 4 + option[3];
222 }
223}
224
225static char **fill_envp(struct d6_packet *packet)
226{
227 char **envp, **curr;
228
229 client6_data.env_ptr = NULL;
230 client6_data.env_idx = 0;
231
232 *new_env() = xasprintf("interface=%s", client_config.interface);
233
234 if (packet)
235 option_to_env(packet->d6_options, packet->d6_options + sizeof(packet->d6_options));
236
237 envp = curr = client6_data.env_ptr;
238 while (*curr)
239 putenv(*curr++);
240
241 return envp;
242}
243
244/* Call a script with a par file and env vars */
245static void d6_run_script(struct d6_packet *packet, const char *name)
246{
247 char **envp, **curr;
248 char *argv[3];
249
250 envp = fill_envp(packet);
251
252 /* call script */
253 log1("Executing %s %s", client_config.script, name);
254 argv[0] = (char*) client_config.script;
255 argv[1] = (char*) name;
256 argv[2] = NULL;
257 spawn_and_wait(argv);
258
259 for (curr = envp; *curr; curr++) {
260 log2(" %s", *curr);
261 bb_unsetenv_and_free(*curr);
262 }
263 free(envp);
264}
265
266
267/*** Sending/receiving packets ***/
268
269static ALWAYS_INLINE uint32_t random_xid(void)
270{
271 uint32_t t = rand() & htonl(0x00ffffff);
272 return t;
273}
274
275/* Initialize the packet with the proper defaults */
276static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid)
277{
278 struct d6_option *clientid;
279
280 memset(packet, 0, sizeof(*packet));
281
282 packet->d6_xid32 = xid;
283 packet->d6_msg_type = type;
284
285 clientid = (void*)client_config.clientid;
286 return d6_store_blob(packet->d6_options, clientid, clientid->len + 2+2);
287}
288
289static uint8_t *add_d6_client_options(uint8_t *ptr)
290{
291 return ptr;
292 //uint8_t c;
293 //int i, end, len;
294
295 /* Add a "param req" option with the list of options we'd like to have
296 * from stubborn DHCP servers. Pull the data from the struct in common.c.
297 * No bounds checking because it goes towards the head of the packet. */
298 //...
299
300 /* Add -x options if any */
301 //...
302}
303
304static int d6_mcast_from_client_config_ifindex(struct d6_packet *packet, uint8_t *end)
305{
306 static const uint8_t FF02__1_2[16] = {
307 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
308 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
309 };
310
311 return d6_send_raw_packet(
312 packet, (end - (uint8_t*) packet),
313 /*src*/ NULL, CLIENT_PORT,
314 /*dst*/ (struct in6_addr*)FF02__1_2, SERVER_PORT, MAC_BCAST_ADDR,
315 client_config.ifindex
316 );
317}
318
319/* Milticast a DHCPv6 Solicit packet to the network, with an optionally requested IP.
320 *
321 * RFC 3315 17.1.1. Creation of Solicit Messages
322 *
323 * The client MUST include a Client Identifier option to identify itself
324 * to the server. The client includes IA options for any IAs to which
325 * it wants the server to assign addresses. The client MAY include
326 * addresses in the IAs as a hint to the server about addresses for
327 * which the client has a preference. ...
328 *
329 * The client uses IA_NA options to request the assignment of non-
330 * temporary addresses and uses IA_TA options to request the assignment
331 * of temporary addresses. Either IA_NA or IA_TA options, or a
332 * combination of both, can be included in DHCP messages.
333 *
334 * The client SHOULD include an Option Request option (see section 22.7)
335 * to indicate the options the client is interested in receiving. The
336 * client MAY additionally include instances of those options that are
337 * identified in the Option Request option, with data values as hints to
338 * the server about parameter values the client would like to have
339 * returned.
340 *
341 * The client includes a Reconfigure Accept option (see section 22.20)
342 * if the client is willing to accept Reconfigure messages from the
343 * server.
344 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
345 | OPTION_CLIENTID | option-len |
346 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
347 . .
348 . DUID .
349 . (variable length) .
350 . .
351 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
352
353
354 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
355 | OPTION_IA_NA | option-len |
356 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
357 | IAID (4 octets) |
358 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
359 | T1 |
360 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
361 | T2 |
362 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
363 | |
364 . IA_NA-options .
365 . .
366 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367
368
369 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
370 | OPTION_IAADDR | option-len |
371 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
372 | |
373 | IPv6 address |
374 | |
375 | |
376 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
377 | preferred-lifetime |
378 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
379 | valid-lifetime |
380 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
381 . .
382 . IAaddr-options .
383 . .
384 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
385
386
387 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
388 | OPTION_ORO | option-len |
389 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
390 | requested-option-code-1 | requested-option-code-2 |
391 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
392 | ... |
393 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
394
395
396 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
397 | OPTION_RECONF_ACCEPT | 0 |
398 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
399 */
400/* NOINLINE: limit stack usage in caller */
401static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ipv6)
402{
403 struct d6_packet packet;
404 uint8_t *opt_ptr;
405 unsigned len;
406
407 /* Fill in: msg type, client id */
408 opt_ptr = init_d6_packet(&packet, D6_MSG_SOLICIT, xid);
409
410 /* Create new IA_NA, optionally with included IAADDR with requested IP */
411 free(client6_data.ia_na);
412 len = requested_ipv6 ? 2+2+4+4+4 + 2+2+16+4+4 : 2+2+4+4+4;
413 client6_data.ia_na = xzalloc(len);
414 client6_data.ia_na->code = D6_OPT_IA_NA;
415 client6_data.ia_na->len = len - 4;
416 *(uint32_t*)client6_data.ia_na->data = rand(); /* IAID */
417 if (requested_ipv6) {
418 struct d6_option *iaaddr = (void*)(client6_data.ia_na->data + 4+4+4);
419 iaaddr->code = D6_OPT_IAADDR;
420 iaaddr->len = 16+4+4;
421 memcpy(iaaddr->data, requested_ipv6, 16);
422 }
423 opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, len);
424
425 /* Add options:
426 * "param req" option according to -O, options specified with -x
427 */
428 opt_ptr = add_d6_client_options(opt_ptr);
429
430 bb_info_msg("Sending discover...");
431 return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
432}
433
434/* Multicast a DHCPv6 request message
435 *
436 * RFC 3315 18.1.1. Creation and Transmission of Request Messages
437 *
438 * The client uses a Request message to populate IAs with addresses and
439 * obtain other configuration information. The client includes one or
440 * more IA options in the Request message. The server then returns
441 * addresses and other information about the IAs to the client in IA
442 * options in a Reply message.
443 *
444 * The client generates a transaction ID and inserts this value in the
445 * "transaction-id" field.
446 *
447 * The client places the identifier of the destination server in a
448 * Server Identifier option.
449 *
450 * The client MUST include a Client Identifier option to identify itself
451 * to the server. The client adds any other appropriate options,
452 * including one or more IA options (if the client is requesting that
453 * the server assign it some network addresses).
454 *
455 * The client MUST include an Option Request option (see section 22.7)
456 * to indicate the options the client is interested in receiving. The
457 * client MAY include options with data values as hints to the server
458 * about parameter values the client would like to have returned.
459 *
460 * The client includes a Reconfigure Accept option (see section 22.20)
461 * indicating whether or not the client is willing to accept Reconfigure
462 * messages from the server.
463 */
464/* NOINLINE: limit stack usage in caller */
465static NOINLINE int send_d6_select(uint32_t xid)
466{
467 struct d6_packet packet;
468 uint8_t *opt_ptr;
469
470 /* Fill in: msg type, client id */
471 opt_ptr = init_d6_packet(&packet, D6_MSG_REQUEST, xid);
472
473 /* server id */
474 opt_ptr = d6_store_blob(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
475 /* IA NA (contains requested IP) */
476 opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
477
478 /* Add options:
479 * "param req" option according to -O, options specified with -x
480 */
481 opt_ptr = add_d6_client_options(opt_ptr);
482
483 bb_info_msg("Sending select...");
484 return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
485}
486
487/* Unicast or broadcast a DHCP renew message
488 *
489 * RFC 3315 18.1.3. Creation and Transmission of Renew Messages
490 *
491 * To extend the valid and preferred lifetimes for the addresses
492 * associated with an IA, the client sends a Renew message to the server
493 * from which the client obtained the addresses in the IA containing an
494 * IA option for the IA. The client includes IA Address options in the
495 * IA option for the addresses associated with the IA. The server
496 * determines new lifetimes for the addresses in the IA according to the
497 * administrative configuration of the server. The server may also add
498 * new addresses to the IA. The server may remove addresses from the IA
499 * by setting the preferred and valid lifetimes of those addresses to
500 * zero.
501 *
502 * The server controls the time at which the client contacts the server
503 * to extend the lifetimes on assigned addresses through the T1 and T2
504 * parameters assigned to an IA.
505 *
506 * At time T1 for an IA, the client initiates a Renew/Reply message
507 * exchange to extend the lifetimes on any addresses in the IA. The
508 * client includes an IA option with all addresses currently assigned to
509 * the IA in its Renew message.
510 *
511 * If T1 or T2 is set to 0 by the server (for an IA_NA) or there are no
512 * T1 or T2 times (for an IA_TA), the client may send a Renew or Rebind
513 * message, respectively, at the client's discretion.
514 *
515 * The client sets the "msg-type" field to RENEW. The client generates
516 * a transaction ID and inserts this value in the "transaction-id"
517 * field.
518 *
519 * The client places the identifier of the destination server in a
520 * Server Identifier option.
521 *
522 * The client MUST include a Client Identifier option to identify itself
523 * to the server. The client adds any appropriate options, including
524 * one or more IA options. The client MUST include the list of
525 * addresses the client currently has associated with the IAs in the
526 * Renew message.
527 *
528 * The client MUST include an Option Request option (see section 22.7)
529 * to indicate the options the client is interested in receiving. The
530 * client MAY include options with data values as hints to the server
531 * about parameter values the client would like to have returned.
532 */
533/* NOINLINE: limit stack usage in caller */
534static NOINLINE int send_d6_renew(uint32_t xid, struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
535{
536 struct d6_packet packet;
537 uint8_t *opt_ptr;
538
539 /* Fill in: msg type, client id */
540 opt_ptr = init_d6_packet(&packet, DHCPREQUEST, xid);
541
542 /* server id */
543 opt_ptr = d6_store_blob(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
544 /* IA NA (contains requested IP) */
545 opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
546
547 /* Add options:
548 * "param req" option according to -O, options specified with -x
549 */
550 opt_ptr = add_d6_client_options(opt_ptr);
551
552 bb_info_msg("Sending renew...");
553 if (server_ipv6)
554 return d6_send_kernel_packet(
555 &packet, (opt_ptr - (uint8_t*) &packet),
556 our_cur_ipv6, CLIENT_PORT,
557 server_ipv6, SERVER_PORT
558 );
559 return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
560}
561
562/* Unicast a DHCP release message */
563static int send_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
564{
565 struct d6_packet packet;
566 uint8_t *opt_ptr;
567
568 /* Fill in: msg type, client id */
569 opt_ptr = init_d6_packet(&packet, D6_MSG_RELEASE, random_xid());
570 /* server id */
571 opt_ptr = d6_store_blob(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
572 /* IA NA (contains our current IP) */
573 opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
574
575 bb_info_msg("Sending release...");
576 return d6_send_kernel_packet(
577 &packet, (opt_ptr - (uint8_t*) &packet),
578 our_cur_ipv6, CLIENT_PORT,
579 server_ipv6, SERVER_PORT
580 );
581}
582
583/* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */
584/* NOINLINE: limit stack usage in caller */
585static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6
586 UNUSED_PARAM
587 , struct d6_packet *d6_pkt, int fd)
588{
589 int bytes;
590 struct ip6_udp_d6_packet packet;
591
592 bytes = safe_read(fd, &packet, sizeof(packet));
593 if (bytes < 0) {
594 log1("Packet read error, ignoring");
595 /* NB: possible down interface, etc. Caller should pause. */
596 return bytes; /* returns -1 */
597 }
598
599 if (bytes < (int) (sizeof(packet.ip6) + sizeof(packet.udp))) {
600 log1("Packet is too short, ignoring");
601 return -2;
602 }
603
604 if (bytes < sizeof(packet.ip6) + ntohs(packet.ip6.ip6_plen)) {
605 /* packet is bigger than sizeof(packet), we did partial read */
606 log1("Oversized packet, ignoring");
607 return -2;
608 }
609
610 /* ignore any extra garbage bytes */
611 bytes = sizeof(packet.ip6) + ntohs(packet.ip6.ip6_plen);
612
613 /* make sure its the right packet for us, and that it passes sanity checks */
614 if (packet.ip6.ip6_nxt != IPPROTO_UDP
615 || (packet.ip6.ip6_vfc >> 4) != 6
616 || packet.udp.dest != htons(CLIENT_PORT)
617 /* || bytes > (int) sizeof(packet) - can't happen */
618 || packet.udp.len != packet.ip6.ip6_plen
619 ) {
620 log1("Unrelated/bogus packet, ignoring");
621 return -2;
622 }
623
624//How to do this for ipv6?
625// /* verify UDP checksum. IP header has to be modified for this */
626// memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
627// /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
628// packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
629// check = packet.udp.check;
630// packet.udp.check = 0;
631// if (check && check != inet_cksum((uint16_t *)&packet, bytes)) {
632// log1("Packet with bad UDP checksum received, ignoring");
633// return -2;
634// }
635
636 log1("Received a packet");
637 d6_dump_packet(&packet.data);
638
639 bytes -= sizeof(packet.ip6) + sizeof(packet.udp);
640 memcpy(d6_pkt, &packet.data, bytes);
641 return bytes;
642}
643
644
645/*** Main ***/
646
647static int sockfd = -1;
648
649#define LISTEN_NONE 0
650#define LISTEN_KERNEL 1
651#define LISTEN_RAW 2
652static smallint listen_mode;
653
654/* initial state: (re)start DHCP negotiation */
655#define INIT_SELECTING 0
656/* discover was sent, DHCPOFFER reply received */
657#define REQUESTING 1
658/* select/renew was sent, DHCPACK reply received */
659#define BOUND 2
660/* half of lease passed, want to renew it by sending unicast renew requests */
661#define RENEWING 3
662/* renew requests were not answered, lease is almost over, send broadcast renew */
663#define REBINDING 4
664/* manually requested renew (SIGUSR1) */
665#define RENEW_REQUESTED 5
666/* release, possibly manually requested (SIGUSR2) */
667#define RELEASED 6
668static smallint state;
669
670static int d6_raw_socket(int ifindex)
671{
672 int fd;
673 struct sockaddr_ll sock;
674
675 /*
676 * Comment:
677 *
678 * I've selected not to see LL header, so BPF doesn't see it, too.
679 * The filter may also pass non-IP and non-ARP packets, but we do
680 * a more complete check when receiving the message in userspace.
681 *
682 * and filter shamelessly stolen from:
683 *
684 * http://www.flamewarmaster.de/software/dhcpclient/
685 *
686 * There are a few other interesting ideas on that page (look under
687 * "Motivation"). Use of netlink events is most interesting. Think
688 * of various network servers listening for events and reconfiguring.
689 * That would obsolete sending HUP signals and/or make use of restarts.
690 *
691 * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
692 * License: GPL v2.
693 *
694 * TODO: make conditional?
695 */
696#if 0
697 static const struct sock_filter filter_instr[] = {
698 /* load 9th byte (protocol) */
699 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
700 /* jump to L1 if it is IPPROTO_UDP, else to L4 */
701 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6),
702 /* L1: load halfword from offset 6 (flags and frag offset) */
703 BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6),
704 /* jump to L4 if any bits in frag offset field are set, else to L2 */
705 BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0),
706 /* L2: skip IP header (load index reg with header len) */
707 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),
708 /* load udp destination port from halfword[header_len + 2] */
709 BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2),
710 /* jump to L3 if udp dport is CLIENT_PORT, else to L4 */
711 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1),
712 /* L3: accept packet */
713 BPF_STMT(BPF_RET|BPF_K, 0xffffffff),
714 /* L4: discard packet */
715 BPF_STMT(BPF_RET|BPF_K, 0),
716 };
717 static const struct sock_fprog filter_prog = {
718 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
719 /* casting const away: */
720 .filter = (struct sock_filter *) filter_instr,
721 };
722#endif
723
724 log1("Opening raw socket on ifindex %d", ifindex); //log2?
725
726 fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6));
727 log1("Got raw socket fd %d", fd); //log2?
728
729 sock.sll_family = AF_PACKET;
730 sock.sll_protocol = htons(ETH_P_IPV6);
731 sock.sll_ifindex = ifindex;
732 xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
733
734#if 0
735 if (CLIENT_PORT == 68) {
736 /* Use only if standard port is in use */
737 /* Ignoring error (kernel may lack support for this) */
738 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
739 sizeof(filter_prog)) >= 0)
740 log1("Attached filter to raw socket fd %d", fd); // log?
741 }
742#endif
743
744 log1("Created raw socket");
745
746 return fd;
747}
748
749static void change_listen_mode(int new_mode)
750{
751 log1("Entering listen mode: %s",
752 new_mode != LISTEN_NONE
753 ? (new_mode == LISTEN_KERNEL ? "kernel" : "raw")
754 : "none"
755 );
756
757 listen_mode = new_mode;
758 if (sockfd >= 0) {
759 close(sockfd);
760 sockfd = -1;
761 }
762 if (new_mode == LISTEN_KERNEL)
763 sockfd = udhcp_listen_socket(/*INADDR_ANY,*/ CLIENT_PORT, client_config.interface);
764 else if (new_mode != LISTEN_NONE)
765 sockfd = d6_raw_socket(client_config.ifindex);
766 /* else LISTEN_NONE: sockfd stays closed */
767}
768
769/* Called only on SIGUSR1 */
770static void perform_renew(void)
771{
772 bb_info_msg("Performing a DHCP renew");
773 switch (state) {
774 case BOUND:
775 change_listen_mode(LISTEN_KERNEL);
776 case RENEWING:
777 case REBINDING:
778 state = RENEW_REQUESTED;
779 break;
780 case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
781 d6_run_script(NULL, "deconfig");
782 case REQUESTING:
783 case RELEASED:
784 change_listen_mode(LISTEN_RAW);
785 state = INIT_SELECTING;
786 break;
787 case INIT_SELECTING:
788 break;
789 }
790}
791
792static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
793{
794 /* send release packet */
795 if (state == BOUND || state == RENEWING || state == REBINDING) {
796 bb_info_msg("Unicasting a release");
797 send_d6_release(server_ipv6, our_cur_ipv6); /* unicast */
798 d6_run_script(NULL, "deconfig");
799 }
800 bb_info_msg("Entering released state");
801
802 change_listen_mode(LISTEN_NONE);
803 state = RELEASED;
804}
805
806///static uint8_t* alloc_dhcp_option(int code, const char *str, int extra)
807///{
808/// uint8_t *storage;
809/// int len = strnlen(str, 255);
810/// storage = xzalloc(len + extra + OPT_DATA);
811/// storage[OPT_CODE] = code;
812/// storage[OPT_LEN] = len + extra;
813/// memcpy(storage + extra + OPT_DATA, str, len);
814/// return storage;
815///}
816
817#if BB_MMU
818static void client_background(void)
819{
820 bb_daemonize(0);
821 logmode &= ~LOGMODE_STDIO;
822 /* rewrite pidfile, as our pid is different now */
823 write_pidfile(client_config.pidfile);
824}
825#endif
826
827//usage:#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
828//usage:# define IF_UDHCP_VERBOSE(...) __VA_ARGS__
829//usage:#else
830//usage:# define IF_UDHCP_VERBOSE(...)
831//usage:#endif
832//usage:#define udhcpc6_trivial_usage
833//usage: "[-fbnq"IF_UDHCP_VERBOSE("v")"oR] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n"
834//usage: " [-x OPT:VAL]... [-O OPT]..." IF_FEATURE_UDHCP_PORT(" [-P N]")
835//usage:#define udhcpc6_full_usage "\n"
836//usage: IF_LONG_OPTS(
837//usage: "\n -i,--interface IFACE Interface to use (default eth0)"
838//usage: "\n -p,--pidfile FILE Create pidfile"
839//usage: "\n -s,--script PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")"
840//usage: "\n -B,--broadcast Request broadcast replies"
841//usage: "\n -t,--retries N Send up to N discover packets"
842//usage: "\n -T,--timeout N Pause between packets (default 3 seconds)"
843//usage: "\n -A,--tryagain N Wait N seconds after failure (default 20)"
844//usage: "\n -f,--foreground Run in foreground"
845//usage: USE_FOR_MMU(
846//usage: "\n -b,--background Background if lease is not obtained"
847//usage: )
848//usage: "\n -n,--now Exit if lease is not obtained"
849//usage: "\n -q,--quit Exit after obtaining lease"
850//usage: "\n -R,--release Release IP on exit"
851//usage: "\n -S,--syslog Log to syslog too"
852//usage: IF_FEATURE_UDHCP_PORT(
853//usage: "\n -P,--client-port N Use port N (default 546)"
854//usage: )
855////usage: IF_FEATURE_UDHCPC_ARPING(
856////usage: "\n -a,--arping Use arping to validate offered address"
857////usage: )
858//usage: "\n -O,--request-option OPT Request option OPT from server (cumulative)"
859//usage: "\n -o,--no-default-options Don't request any options (unless -O is given)"
860//usage: "\n -r,--request IP Request this IP address"
861//usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)"
862//usage: "\n Examples of string, numeric, and hex byte opts:"
863//usage: "\n -x hostname:bbox - option 12"
864//usage: "\n -x lease:3600 - option 51 (lease time)"
865//usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)"
866//usage: IF_UDHCP_VERBOSE(
867//usage: "\n -v Verbose"
868//usage: )
869//usage: )
870//usage: IF_NOT_LONG_OPTS(
871//usage: "\n -i IFACE Interface to use (default eth0)"
872//usage: "\n -p FILE Create pidfile"
873//usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")"
874//usage: "\n -B Request broadcast replies"
875//usage: "\n -t N Send up to N discover packets"
876//usage: "\n -T N Pause between packets (default 3 seconds)"
877//usage: "\n -A N Wait N seconds (default 20) after failure"
878//usage: "\n -f Run in foreground"
879//usage: USE_FOR_MMU(
880//usage: "\n -b Background if lease is not obtained"
881//usage: )
882//usage: "\n -n Exit if lease is not obtained"
883//usage: "\n -q Exit after obtaining lease"
884//usage: "\n -R Release IP on exit"
885//usage: "\n -S Log to syslog too"
886//usage: IF_FEATURE_UDHCP_PORT(
887//usage: "\n -P N Use port N (default 546)"
888//usage: )
889////usage: IF_FEATURE_UDHCPC_ARPING(
890////usage: "\n -a Use arping to validate offered address"
891////usage: )
892//usage: "\n -O OPT Request option OPT from server (cumulative)"
893//usage: "\n -o Don't request any options (unless -O is given)"
894//usage: "\n -r IP Request this IP address"
895//usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)"
896//usage: "\n Examples of string, numeric, and hex byte opts:"
897//usage: "\n -x hostname:bbox - option 12"
898//usage: "\n -x lease:3600 - option 51 (lease time)"
899//usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)"
900//usage: IF_UDHCP_VERBOSE(
901//usage: "\n -v Verbose"
902//usage: )
903//usage: )
904//usage: "\nSignals:"
905//usage: "\n USR1 Renew lease"
906//usage: "\n USR2 Release lease"
907
908
909int udhcpc6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
910int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
911{
912 const char *str_r;
913 IF_FEATURE_UDHCP_PORT(char *str_P;)
914 void *clientid_mac_ptr;
915 llist_t *list_O = NULL;
916 llist_t *list_x = NULL;
917 int tryagain_timeout = 20;
918 int discover_timeout = 3;
919 int discover_retries = 3;
920 struct in6_addr srv6_buf;
921 struct in6_addr ipv6_buf;
922 struct in6_addr *requested_ipv6;
923 uint32_t xid = 0;
924 int packet_num;
925 int timeout; /* must be signed */
926 unsigned already_waited_sec;
927 unsigned opt;
928 int max_fd;
929 int retval;
930 fd_set rfds;
931
932 /* Default options */
933 IF_FEATURE_UDHCP_PORT(SERVER_PORT = 547;)
934 IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 546;)
935 client_config.interface = "eth0";
936 client_config.script = CONFIG_UDHCPC_DEFAULT_SCRIPT;
937
938 /* Parse command line */
939 /* O,x: list; -T,-t,-A take numeric param */
940 opt_complementary = "O::x::T+:t+:A+" IF_UDHCP_VERBOSE(":vv") ;
941 IF_LONG_OPTS(applet_long_options = udhcpc6_longopts;)
942 opt = getopt32(argv, "i:np:qRr:s:T:t:SA:O:ox:f"
943 USE_FOR_MMU("b")
944 ///IF_FEATURE_UDHCPC_ARPING("a")
945 IF_FEATURE_UDHCP_PORT("P:")
946 "v"
947 , &client_config.interface, &client_config.pidfile, &str_r /* i,p */
948 , &client_config.script /* s */
949 , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */
950 , &list_O
951 , &list_x
952 IF_FEATURE_UDHCP_PORT(, &str_P)
953 IF_UDHCP_VERBOSE(, &dhcp_verbose)
954 );
955 requested_ipv6 = NULL;
956 if (opt & OPT_r) {
957 if (inet_pton(AF_INET6, str_r, &ipv6_buf) <= 0)
958 bb_error_msg_and_die("bad IPv6 address '%s'", str_r);
959 requested_ipv6 = &ipv6_buf;
960 }
961#if ENABLE_FEATURE_UDHCP_PORT
962 if (opt & OPT_P) {
963 CLIENT_PORT = xatou16(str_P);
964 SERVER_PORT = CLIENT_PORT - 1;
965 }
966#endif
967 if (opt & OPT_o)
968 client_config.no_default_options = 1;
969 while (list_O) {
970 char *optstr = llist_pop(&list_O);
971 unsigned n = bb_strtou(optstr, NULL, 0);
972 if (errno || n > 254) {
973 n = udhcp_option_idx(optstr);
974 n = dhcp_optflags[n].code;
975 }
976 client_config.opt_mask[n >> 3] |= 1 << (n & 7);
977 }
978 while (list_x) {
979 char *optstr = llist_pop(&list_x);
980 char *colon = strchr(optstr, ':');
981 if (colon)
982 *colon = ' ';
983 /* now it looks similar to udhcpd's config file line:
984 * "optname optval", using the common routine: */
985 udhcp_str2optset(optstr, &client_config.options);
986 }
987
988 if (udhcp_read_interface(client_config.interface,
989 &client_config.ifindex,
990 NULL,
991 client_config.client_mac)
992 ) {
993 return 1;
994 }
995
996 /* Create client ID based on mac, set clientid_mac_ptr */
997 {
998 struct d6_option *clientid;
999 clientid = xzalloc(2+2+2+2+6);
1000 clientid->code = D6_OPT_CLIENTID;
1001 clientid->len = 2+2+6;
1002 clientid->data[1] = 3; /* DUID-LL */
1003 clientid->data[3] = 1; /* ethernet */
1004 clientid_mac_ptr = clientid->data + 2+2;
1005 memcpy(clientid_mac_ptr, client_config.client_mac, 6);
1006 client_config.clientid = (void*)clientid;
1007 }
1008
1009#if !BB_MMU
1010 /* on NOMMU reexec (i.e., background) early */
1011 if (!(opt & OPT_f)) {
1012 bb_daemonize_or_rexec(0 /* flags */, argv);
1013 logmode = LOGMODE_NONE;
1014 }
1015#endif
1016 if (opt & OPT_S) {
1017 openlog(applet_name, LOG_PID, LOG_DAEMON);
1018 logmode |= LOGMODE_SYSLOG;
1019 }
1020
1021 /* Make sure fd 0,1,2 are open */
1022 bb_sanitize_stdio();
1023 /* Equivalent of doing a fflush after every \n */
1024 setlinebuf(stdout);
1025 /* Create pidfile */
1026 write_pidfile(client_config.pidfile);
1027 /* Goes to stdout (unless NOMMU) and possibly syslog */
1028 bb_info_msg("%s (v"BB_VER") started", applet_name);
1029 /* Set up the signal pipe */
1030 udhcp_sp_setup();
1031 /* We want random_xid to be random... */
1032 srand(monotonic_us());
1033
1034 state = INIT_SELECTING;
1035 d6_run_script(NULL, "deconfig");
1036 change_listen_mode(LISTEN_RAW);
1037 packet_num = 0;
1038 timeout = 0;
1039 already_waited_sec = 0;
1040
1041 /* Main event loop. select() waits on signal pipe and possibly
1042 * on sockfd.
1043 * "continue" statements in code below jump to the top of the loop.
1044 */
1045 for (;;) {
1046 struct timeval tv;
1047 struct d6_packet packet;
1048 uint8_t *packet_end;
1049 /* silence "uninitialized!" warning */
1050 unsigned timestamp_before_wait = timestamp_before_wait;
1051
1052 //bb_error_msg("sockfd:%d, listen_mode:%d", sockfd, listen_mode);
1053
1054 /* Was opening raw or udp socket here
1055 * if (listen_mode != LISTEN_NONE && sockfd < 0),
1056 * but on fast network renew responses return faster
1057 * than we open sockets. Thus this code is moved
1058 * to change_listen_mode(). Thus we open listen socket
1059 * BEFORE we send renew request (see "case BOUND:"). */
1060
1061 max_fd = udhcp_sp_fd_set(&rfds, sockfd);
1062
1063 tv.tv_sec = timeout - already_waited_sec;
1064 tv.tv_usec = 0;
1065 retval = 0;
1066 /* If we already timed out, fall through with retval = 0, else... */
1067 if ((int)tv.tv_sec > 0) {
1068 timestamp_before_wait = (unsigned)monotonic_sec();
1069 log1("Waiting on select...");
1070 retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
1071 if (retval < 0) {
1072 /* EINTR? A signal was caught, don't panic */
1073 if (errno == EINTR) {
1074 already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
1075 continue;
1076 }
1077 /* Else: an error occured, panic! */
1078 bb_perror_msg_and_die("select");
1079 }
1080 }
1081
1082 /* If timeout dropped to zero, time to become active:
1083 * resend discover/renew/whatever
1084 */
1085 if (retval == 0) {
1086 /* When running on a bridge, the ifindex may have changed
1087 * (e.g. if member interfaces were added/removed
1088 * or if the status of the bridge changed).
1089 * Refresh ifindex and client_mac:
1090 */
1091 if (udhcp_read_interface(client_config.interface,
1092 &client_config.ifindex,
1093 NULL,
1094 client_config.client_mac)
1095 ) {
1096 goto ret0; /* iface is gone? */
1097 }
1098 memcpy(clientid_mac_ptr, client_config.client_mac, 6);
1099
1100 /* We will restart the wait in any case */
1101 already_waited_sec = 0;
1102
1103 switch (state) {
1104 case INIT_SELECTING:
1105 if (packet_num < discover_retries) {
1106 if (packet_num == 0)
1107 xid = random_xid();
1108 /* multicast */
1109 send_d6_discover(xid, requested_ipv6);
1110 timeout = discover_timeout;
1111 packet_num++;
1112 continue;
1113 }
1114 leasefail:
1115 d6_run_script(NULL, "leasefail");
1116#if BB_MMU /* -b is not supported on NOMMU */
1117 if (opt & OPT_b) { /* background if no lease */
1118 bb_info_msg("No lease, forking to background");
1119 client_background();
1120 /* do not background again! */
1121 opt = ((opt & ~OPT_b) | OPT_f);
1122 } else
1123#endif
1124 if (opt & OPT_n) { /* abort if no lease */
1125 bb_info_msg("No lease, failing");
1126 retval = 1;
1127 goto ret;
1128 }
1129 /* wait before trying again */
1130 timeout = tryagain_timeout;
1131 packet_num = 0;
1132 continue;
1133 case REQUESTING:
1134 if (packet_num < discover_retries) {
1135 /* send multicast select packet */
1136 send_d6_select(xid);
1137 timeout = discover_timeout;
1138 packet_num++;
1139 continue;
1140 }
1141 /* Timed out, go back to init state.
1142 * "discover...select...discover..." loops
1143 * were seen in the wild. Treat them similarly
1144 * to "no response to discover" case */
1145 change_listen_mode(LISTEN_RAW);
1146 state = INIT_SELECTING;
1147 goto leasefail;
1148 case BOUND:
1149 /* 1/2 lease passed, enter renewing state */
1150 state = RENEWING;
1151 client_config.first_secs = 0; /* make secs field count from 0 */
1152 change_listen_mode(LISTEN_KERNEL);
1153 log1("Entering renew state");
1154 /* fall right through */
1155 case RENEW_REQUESTED: /* manual (SIGUSR1) renew */
1156 case_RENEW_REQUESTED:
1157 case RENEWING:
1158 if (timeout > 60) {
1159 /* send an unicast renew request */
1160 /* Sometimes observed to fail (EADDRNOTAVAIL) to bind
1161 * a new UDP socket for sending inside send_renew.
1162 * I hazard to guess existing listening socket
1163 * is somehow conflicting with it, but why is it
1164 * not deterministic then?! Strange.
1165 * Anyway, it does recover by eventually failing through
1166 * into INIT_SELECTING state.
1167 */
1168 send_d6_renew(xid, &srv6_buf, requested_ipv6);
1169 timeout >>= 1;
1170 continue;
1171 }
1172 /* Timed out, enter rebinding state */
1173 log1("Entering rebinding state");
1174 state = REBINDING;
1175 /* fall right through */
1176 case REBINDING:
1177 /* Switch to bcast receive */
1178 change_listen_mode(LISTEN_RAW);
1179 /* Lease is *really* about to run out,
1180 * try to find DHCP server using broadcast */
1181 if (timeout > 0) {
1182 /* send a broadcast renew request */
1183 send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6);
1184 timeout >>= 1;
1185 continue;
1186 }
1187 /* Timed out, enter init state */
1188 bb_info_msg("Lease lost, entering init state");
1189 d6_run_script(NULL, "deconfig");
1190 state = INIT_SELECTING;
1191 client_config.first_secs = 0; /* make secs field count from 0 */
1192 /*timeout = 0; - already is */
1193 packet_num = 0;
1194 continue;
1195 /* case RELEASED: */
1196 }
1197 /* yah, I know, *you* say it would never happen */
1198 timeout = INT_MAX;
1199 continue; /* back to main loop */
1200 } /* if select timed out */
1201
1202 /* select() didn't timeout, something happened */
1203
1204 /* Is it a signal? */
1205 /* note: udhcp_sp_read checks FD_ISSET before reading */
1206 switch (udhcp_sp_read(&rfds)) {
1207 case SIGUSR1:
1208 client_config.first_secs = 0; /* make secs field count from 0 */
1209 already_waited_sec = 0;
1210 perform_renew();
1211 if (state == RENEW_REQUESTED) {
1212 /* We might be either on the same network
1213 * (in which case renew might work),
1214 * or we might be on a completely different one
1215 * (in which case renew won't ever succeed).
1216 * For the second case, must make sure timeout
1217 * is not too big, or else we can send
1218 * futile renew requests for hours.
1219 * (Ab)use -A TIMEOUT value (usually 20 sec)
1220 * as a cap on the timeout.
1221 */
1222 if (timeout > tryagain_timeout)
1223 timeout = tryagain_timeout;
1224 goto case_RENEW_REQUESTED;
1225 }
1226 /* Start things over */
1227 packet_num = 0;
1228 /* Kill any timeouts, user wants this to hurry along */
1229 timeout = 0;
1230 continue;
1231 case SIGUSR2:
1232 perform_d6_release(&srv6_buf, requested_ipv6);
1233 timeout = INT_MAX;
1234 continue;
1235 case SIGTERM:
1236 bb_info_msg("Received SIGTERM");
1237 goto ret0;
1238 }
1239
1240 /* Is it a packet? */
1241 if (listen_mode == LISTEN_NONE || !FD_ISSET(sockfd, &rfds))
1242 continue; /* no */
1243
1244 {
1245 int len;
1246
1247 /* A packet is ready, read it */
1248 if (listen_mode == LISTEN_KERNEL)
1249 len = d6_recv_kernel_packet(&srv6_buf, &packet, sockfd);
1250 else
1251 len = d6_recv_raw_packet(&srv6_buf, &packet, sockfd);
1252 if (len == -1) {
1253 /* Error is severe, reopen socket */
1254 bb_info_msg("Read error: %s, reopening socket", strerror(errno));
1255 sleep(discover_timeout); /* 3 seconds by default */
1256 change_listen_mode(listen_mode); /* just close and reopen */
1257 }
1258 /* If this packet will turn out to be unrelated/bogus,
1259 * we will go back and wait for next one.
1260 * Be sure timeout is properly decreased. */
1261 already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
1262 if (len < 0)
1263 continue;
1264 packet_end = (uint8_t*)&packet + len;
1265 }
1266
1267 if ((packet.d6_xid32 & htonl(0x00ffffff)) != xid) {
1268 log1("xid %x (our is %x), ignoring packet",
1269 (unsigned)(packet.d6_xid32 & htonl(0x00ffffff)), (unsigned)xid);
1270 continue;
1271 }
1272
1273 switch (state) {
1274 case INIT_SELECTING:
1275 if (packet.d6_msg_type == D6_MSG_ADVERTISE)
1276 goto type_is_ok;
1277 /* DHCPv6 has "Rapid Commit", when instead of Advertise,
1278 * server sends Reply right away.
1279 * Fall through to check for this case.
1280 */
1281 case REQUESTING:
1282 case RENEWING:
1283 case RENEW_REQUESTED:
1284 case REBINDING:
1285 if (packet.d6_msg_type == D6_MSG_REPLY) {
1286 uint32_t lease_seconds;
1287 struct d6_option *option, *iaaddr;
1288 type_is_ok:
1289 option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
1290 if (option && option->data[4] != 0) {
1291 /* return to init state */
1292 bb_info_msg("Received DHCP NAK (%u)", option->data[4]);
1293 d6_run_script(&packet, "nak");
1294 if (state != REQUESTING)
1295 d6_run_script(NULL, "deconfig");
1296 change_listen_mode(LISTEN_RAW);
1297 sleep(3); /* avoid excessive network traffic */
1298 state = INIT_SELECTING;
1299 client_config.first_secs = 0; /* make secs field count from 0 */
1300 requested_ipv6 = NULL;
1301 timeout = 0;
1302 packet_num = 0;
1303 already_waited_sec = 0;
1304 continue;
1305 }
1306 option = d6_copy_option(packet.d6_options, packet_end, D6_OPT_SERVERID);
1307 if (!option) {
1308 bb_error_msg("no server ID, ignoring packet");
1309 continue;
1310 /* still selecting - this server looks bad */
1311 }
1312//Note: we do not bother comparing server IDs in Advertise and Reply msgs.
1313//server_id variable is used solely for creation of proper server_id option
1314//in outgoing packets. (why DHCPv6 even introduced it is a mystery).
1315 free(client6_data.server_id);
1316 client6_data.server_id = option;
1317 if (packet.d6_msg_type == D6_MSG_ADVERTISE) {
1318 /* enter requesting state */
1319 state = REQUESTING;
1320 timeout = 0;
1321 packet_num = 0;
1322 already_waited_sec = 0;
1323 continue;
1324 }
1325 /* It's a D6_MSG_REPLY */
1326/*
1327 * RFC 3315 18.1.8. Receipt of Reply Messages
1328 *
1329 * Upon the receipt of a valid Reply message in response to a Solicit
1330 * (with a Rapid Commit option), Request, Confirm, Renew, Rebind or
1331 * Information-request message, the client extracts the configuration
1332 * information contained in the Reply. The client MAY choose to report
1333 * any status code or message from the status code option in the Reply
1334 * message.
1335 *
1336 * The client SHOULD perform duplicate address detection [17] on each of
1337 * the addresses in any IAs it receives in the Reply message before
1338 * using that address for traffic. If any of the addresses are found to
1339 * be in use on the link, the client sends a Decline message to the
1340 * server as described in section 18.1.7.
1341 *
1342 * If the Reply was received in response to a Solicit (with a Rapid
1343 * Commit option), Request, Renew or Rebind message, the client updates
1344 * the information it has recorded about IAs from the IA options
1345 * contained in the Reply message:
1346 *
1347 * - Record T1 and T2 times.
1348 *
1349 * - Add any new addresses in the IA option to the IA as recorded by
1350 * the client.
1351 *
1352 * - Update lifetimes for any addresses in the IA option that the
1353 * client already has recorded in the IA.
1354 *
1355 * - Discard any addresses from the IA, as recorded by the client, that
1356 * have a valid lifetime of 0 in the IA Address option.
1357 *
1358 * - Leave unchanged any information about addresses the client has
1359 * recorded in the IA but that were not included in the IA from the
1360 * server.
1361 *
1362 * Management of the specific configuration information is detailed in
1363 * the definition of each option in section 22.
1364 *
1365 * If the client receives a Reply message with a Status Code containing
1366 * UnspecFail, the server is indicating that it was unable to process
1367 * the message due to an unspecified failure condition. If the client
1368 * retransmits the original message to the same server to retry the
1369 * desired operation, the client MUST limit the rate at which it
1370 * retransmits the message and limit the duration of the time during
1371 * which it retransmits the message.
1372 *
1373 * When the client receives a Reply message with a Status Code option
1374 * with the value UseMulticast, the client records the receipt of the
1375 * message and sends subsequent messages to the server through the
1376 * interface on which the message was received using multicast. The
1377 * client resends the original message using multicast.
1378 *
1379 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1380 * | OPTION_IA_NA | option-len |
1381 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1382 * | IAID (4 octets) |
1383 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1384 * | T1 |
1385 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1386 * | T2 |
1387 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1388 * | |
1389 * . IA_NA-options .
1390 * . .
1391 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1392 *
1393 *
1394 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1395 * | OPTION_IAADDR | option-len |
1396 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1397 * | |
1398 * | IPv6 address |
1399 * | |
1400 * | |
1401 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1402 * | preferred-lifetime |
1403 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1404 * | valid-lifetime |
1405 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1406 * . .
1407 * . IAaddr-options .
1408 * . .
1409 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1410 */
1411 free(client6_data.ia_na);
1412 client6_data.ia_na = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_NA);
1413 if (!client6_data.ia_na) {
1414 bb_error_msg("no %s option, ignoring packet", "IA_NA");
1415 continue;
1416 }
1417 if (client6_data.ia_na->len < (4 + 4 + 4) + (2 + 2 + 16 + 4 + 4)) {
1418 bb_error_msg("IA_NA option is too short:%d bytes", client6_data.ia_na->len);
1419 continue;
1420 }
1421 iaaddr = d6_find_option(client6_data.ia_na->data + 4 + 4 + 4,
1422 client6_data.ia_na->data + client6_data.ia_na->len,
1423 D6_OPT_IAADDR
1424 );
1425 if (!iaaddr) {
1426 bb_error_msg("no %s option, ignoring packet", "IAADDR");
1427 continue;
1428 }
1429 if (iaaddr->len < (16 + 4 + 4)) {
1430 bb_error_msg("IAADDR option is too short:%d bytes", iaaddr->len);
1431 continue;
1432 }
1433 /* Note: the address is sufficiently aligned for cast:
1434 * we _copied_ IA-NA, and copy is always well-aligned.
1435 */
1436 requested_ipv6 = (struct in6_addr*) iaaddr->data;
1437 move_from_unaligned32(lease_seconds, iaaddr->data + 16 + 4);
1438 lease_seconds = ntohl(lease_seconds);
1439 /* paranoia: must not be too small and not prone to overflows */
1440 if (lease_seconds < 0x10)
1441 lease_seconds = 0x10;
1442/// TODO: check for 0 lease time?
1443 if (lease_seconds >= 0x10000000)
1444 lease_seconds = 0x0fffffff;
1445 /* enter bound state */
1446 timeout = lease_seconds / 2;
1447 bb_info_msg("Lease obtained, lease time %u",
1448 /*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds);
1449 d6_run_script(&packet, state == REQUESTING ? "bound" : "renew");
1450
1451 state = BOUND;
1452 change_listen_mode(LISTEN_NONE);
1453 if (opt & OPT_q) { /* quit after lease */
1454 goto ret0;
1455 }
1456 /* future renew failures should not exit (JM) */
1457 opt &= ~OPT_n;
1458#if BB_MMU /* NOMMU case backgrounded earlier */
1459 if (!(opt & OPT_f)) {
1460 client_background();
1461 /* do not background again! */
1462 opt = ((opt & ~OPT_b) | OPT_f);
1463 }
1464#endif
1465 already_waited_sec = 0;
1466 continue; /* back to main loop */
1467 }
1468 continue;
1469 /* case BOUND: - ignore all packets */
1470 /* case RELEASED: - ignore all packets */
1471 }
1472 /* back to main loop */
1473 } /* for (;;) - main loop ends */
1474
1475 ret0:
1476 if (opt & OPT_R) /* release on quit */
1477 perform_d6_release(&srv6_buf, requested_ipv6);
1478 retval = 0;
1479 ret:
1480 /*if (client_config.pidfile) - remove_pidfile has its own check */
1481 remove_pidfile(client_config.pidfile);
1482 return retval;
1483}
diff --git a/networking/udhcp/d6_packet.c b/networking/udhcp/d6_packet.c
new file mode 100644
index 000000000..79b2946ef
--- /dev/null
+++ b/networking/udhcp/d6_packet.c
@@ -0,0 +1,172 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 2011 Denys Vlasenko.
4 *
5 * Licensed under GPLv2, see file LICENSE in this source tree.
6 */
7#include "common.h"
8#include "d6_common.h"
9#include "dhcpd.h"
10#include <netinet/in.h>
11#include <netinet/if_ether.h>
12#include <netpacket/packet.h>
13
14#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
15void FAST_FUNC d6_dump_packet(struct d6_packet *packet)
16{
17 if (dhcp_verbose < 2)
18 return;
19
20 bb_info_msg(
21 " xid %x"
22 , packet->d6_xid32
23 );
24 //*bin2hex(buf, (void *) packet->chaddr, sizeof(packet->chaddr)) = '\0';
25 //bb_info_msg(" chaddr %s", buf);
26}
27#endif
28
29int FAST_FUNC d6_recv_kernel_packet(struct in6_addr *peer_ipv6
30 UNUSED_PARAM
31 , struct d6_packet *packet, int fd)
32{
33 int bytes;
34
35 memset(packet, 0, sizeof(*packet));
36 bytes = safe_read(fd, packet, sizeof(*packet));
37 if (bytes < 0) {
38 log1("Packet read error, ignoring");
39 return bytes; /* returns -1 */
40 }
41
42 if (bytes < offsetof(struct d6_packet, d6_options)) {
43 bb_info_msg("Packet with bad magic, ignoring");
44 return -2;
45 }
46 log1("Received a packet");
47 d6_dump_packet(packet);
48
49 return bytes;
50}
51
52/* Construct a ipv6+udp header for a packet, send packet */
53int FAST_FUNC d6_send_raw_packet(
54 struct d6_packet *d6_pkt, unsigned d6_pkt_size,
55 struct in6_addr *src_ipv6, int source_port,
56 struct in6_addr *dst_ipv6, int dest_port, const uint8_t *dest_arp,
57 int ifindex)
58{
59 struct sockaddr_ll dest_sll;
60 struct ip6_udp_d6_packet packet;
61 int fd;
62 int result = -1;
63 const char *msg;
64
65 fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6));
66 if (fd < 0) {
67 msg = "socket(%s)";
68 goto ret_msg;
69 }
70
71 memset(&dest_sll, 0, sizeof(dest_sll));
72 memset(&packet, 0, offsetof(struct ip6_udp_d6_packet, data));
73 packet.data = *d6_pkt; /* struct copy */
74
75 dest_sll.sll_family = AF_PACKET;
76 dest_sll.sll_protocol = htons(ETH_P_IPV6);
77 dest_sll.sll_ifindex = ifindex;
78 dest_sll.sll_halen = 6;
79 memcpy(dest_sll.sll_addr, dest_arp, 6);
80
81 if (bind(fd, (struct sockaddr *)&dest_sll, sizeof(dest_sll)) < 0) {
82 msg = "bind(%s)";
83 goto ret_close;
84 }
85
86 packet.ip6.ip6_vfc = (6 << 4); /* 4 bits version, top 4 bits of tclass */
87 if (src_ipv6)
88 packet.ip6.ip6_src = *src_ipv6; /* struct copy */
89 packet.ip6.ip6_dst = *dst_ipv6; /* struct copy */
90 packet.udp.source = htons(source_port);
91 packet.udp.dest = htons(dest_port);
92 /* size, excluding IP header: */
93 packet.udp.len = htons(sizeof(struct udphdr) + d6_pkt_size);
94 packet.ip6.ip6_plen = packet.udp.len;
95 /*
96 * Someone was smoking weed (at least) while inventing UDP checksumming:
97 * UDP checksum skips first four bytes of IPv6 header.
98 * 'next header' field should be summed as if it is one more byte
99 * to the right, therefore we write its value (IPPROTO_UDP)
100 * into ip6_hlim, and its 'real' location remains zero-filled for now.
101 */
102 packet.ip6.ip6_hlim = IPPROTO_UDP;
103 packet.udp.check = inet_cksum(
104 (uint16_t *)&packet + 2,
105 offsetof(struct ip6_udp_d6_packet, data) - 4 + d6_pkt_size
106 );
107 /* fix 'hop limit' and 'next header' after UDP checksumming */
108 packet.ip6.ip6_hlim = 1; /* observed Windows machines to use hlim=1 */
109 packet.ip6.ip6_nxt = IPPROTO_UDP;
110
111 d6_dump_packet(d6_pkt);
112 result = sendto(fd, &packet, offsetof(struct ip6_udp_d6_packet, data) + d6_pkt_size,
113 /*flags:*/ 0,
114 (struct sockaddr *) &dest_sll, sizeof(dest_sll)
115 );
116 msg = "sendto";
117 ret_close:
118 close(fd);
119 if (result < 0) {
120 ret_msg:
121 bb_perror_msg(msg, "PACKET");
122 }
123 return result;
124}
125
126/* Let the kernel do all the work for packet generation */
127int FAST_FUNC d6_send_kernel_packet(
128 struct d6_packet *d6_pkt, unsigned d6_pkt_size,
129 struct in6_addr *src_ipv6, int source_port,
130 struct in6_addr *dst_ipv6, int dest_port)
131{
132 struct sockaddr_in6 sa;
133 int fd;
134 int result = -1;
135 const char *msg;
136
137 fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
138 if (fd < 0) {
139 msg = "socket(%s)";
140 goto ret_msg;
141 }
142 setsockopt_reuseaddr(fd);
143
144 memset(&sa, 0, sizeof(sa));
145 sa.sin6_family = AF_INET6;
146 sa.sin6_port = htons(source_port);
147 sa.sin6_addr = *src_ipv6; /* struct copy */
148 if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
149 msg = "bind(%s)";
150 goto ret_close;
151 }
152
153 memset(&sa, 0, sizeof(sa));
154 sa.sin6_family = AF_INET6;
155 sa.sin6_port = htons(dest_port);
156 sa.sin6_addr = *dst_ipv6; /* struct copy */
157 if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
158 msg = "connect";
159 goto ret_close;
160 }
161
162 d6_dump_packet(d6_pkt);
163 result = safe_write(fd, d6_pkt, d6_pkt_size);
164 msg = "write";
165 ret_close:
166 close(fd);
167 if (result < 0) {
168 ret_msg:
169 bb_perror_msg(msg, "UDP");
170 }
171 return result;
172}
diff --git a/networking/udhcp/d6_socket.c b/networking/udhcp/d6_socket.c
new file mode 100644
index 000000000..56f69f6a1
--- /dev/null
+++ b/networking/udhcp/d6_socket.c
@@ -0,0 +1,34 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 2011 Denys Vlasenko.
4 *
5 * Licensed under GPLv2, see file LICENSE in this source tree.
6 */
7#include "common.h"
8#include "d6_common.h"
9#include <net/if.h>
10
11int FAST_FUNC d6_listen_socket(int port, const char *inf)
12{
13 int fd;
14 struct sockaddr_in6 addr;
15
16 log1("Opening listen socket on *:%d %s", port, inf);
17 fd = xsocket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
18
19 setsockopt_reuseaddr(fd);
20 if (setsockopt_broadcast(fd) == -1)
21 bb_perror_msg_and_die("SO_BROADCAST");
22
23 /* NB: bug 1032 says this doesn't work on ethernet aliases (ethN:M) */
24 if (setsockopt_bindtodevice(fd, inf))
25 xfunc_die(); /* warning is already printed */
26
27 memset(&addr, 0, sizeof(addr));
28 addr.sin6_family = AF_INET6;
29 addr.sin6_port = htons(port);
30 /* addr.sin6_addr is all-zeros */
31 xbind(fd, (struct sockaddr *)&addr, sizeof(addr));
32
33 return fd;
34}
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index 3d4c397ff..2f2016cd5 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -29,7 +29,7 @@
29#include <netpacket/packet.h> 29#include <netpacket/packet.h>
30#include <linux/filter.h> 30#include <linux/filter.h>
31 31
32/* struct client_config_t client_config is in bb_common_bufsiz1 */ 32/* "struct client_config_t client_config" is in bb_common_bufsiz1 */
33 33
34 34
35#if ENABLE_LONG_OPTS 35#if ENABLE_LONG_OPTS
@@ -46,7 +46,6 @@ static const char udhcpc_longopts[] ALIGN1 =
46 "request\0" Required_argument "r" 46 "request\0" Required_argument "r"
47 "script\0" Required_argument "s" 47 "script\0" Required_argument "s"
48 "timeout\0" Required_argument "T" 48 "timeout\0" Required_argument "T"
49 "version\0" No_argument "v"
50 "retries\0" Required_argument "t" 49 "retries\0" Required_argument "t"
51 "tryagain\0" Required_argument "A" 50 "tryagain\0" Required_argument "A"
52 "syslog\0" No_argument "S" 51 "syslog\0" No_argument "S"
@@ -124,24 +123,6 @@ static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
124 return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]); 123 return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
125} 124}
126 125
127static int sprint_nip6(char *dest, /*const char *pre,*/ const uint8_t *ip)
128{
129 char hexstrbuf[16 * 2];
130 bin2hex(hexstrbuf, (void*)ip, 16);
131 return sprintf(dest, /* "%s" */
132 "%.4s:%.4s:%.4s:%.4s:%.4s:%.4s:%.4s:%.4s",
133 /* pre, */
134 hexstrbuf + 0 * 4,
135 hexstrbuf + 1 * 4,
136 hexstrbuf + 2 * 4,
137 hexstrbuf + 3 * 4,
138 hexstrbuf + 4 * 4,
139 hexstrbuf + 5 * 4,
140 hexstrbuf + 6 * 4,
141 hexstrbuf + 7 * 4
142 );
143}
144
145/* really simple implementation, just count the bits */ 126/* really simple implementation, just count the bits */
146static int mton(uint32_t mask) 127static int mton(uint32_t mask)
147{ 128{
@@ -154,6 +135,63 @@ static int mton(uint32_t mask)
154 return i; 135 return i;
155} 136}
156 137
138/* Check if a given label represents a valid DNS label
139 * Return pointer to the first character after the label upon success,
140 * NULL otherwise.
141 * See RFC1035, 2.3.1
142 */
143/* We don't need to be particularly anal. For example, allowing _, hyphen
144 * at the end, or leading and trailing dots would be ok, since it
145 * can't be used for attacks. (Leading hyphen can be, if someone uses
146 * cmd "$hostname"
147 * in the script: then hostname may be treated as an option)
148 */
149static const char *valid_domain_label(const char *label)
150{
151 unsigned char ch;
152 unsigned pos = 0;
153
154 for (;;) {
155 ch = *label;
156 if ((ch|0x20) < 'a' || (ch|0x20) > 'z') {
157 if (pos == 0) {
158 /* label must begin with letter */
159 return NULL;
160 }
161 if (ch < '0' || ch > '9') {
162 if (ch == '\0' || ch == '.')
163 return label;
164 /* DNS allows only '-', but we are more permissive */
165 if (ch != '-' && ch != '_')
166 return NULL;
167 }
168 }
169 label++;
170 pos++;
171 //Do we want this?
172 //if (pos > 63) /* NS_MAXLABEL; labels must be 63 chars or less */
173 // return NULL;
174 }
175}
176
177/* Check if a given name represents a valid DNS name */
178/* See RFC1035, 2.3.1 */
179static int good_hostname(const char *name)
180{
181 //const char *start = name;
182
183 for (;;) {
184 name = valid_domain_label(name);
185 if (!name)
186 return 0;
187 if (!name[0])
188 return 1;
189 //Do we want this?
190 //return ((name - start) < 1025); /* NS_MAXDNAME */
191 name++;
192 }
193}
194
157/* Create "opt_name=opt_value" string */ 195/* Create "opt_name=opt_value" string */
158static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_optflag *optflag, const char *opt_name) 196static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_optflag *optflag, const char *opt_name)
159{ 197{
@@ -206,8 +244,11 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
206 * the case of list of options. 244 * the case of list of options.
207 */ 245 */
208 case OPTION_STRING: 246 case OPTION_STRING:
247 case OPTION_STRING_HOST:
209 memcpy(dest, option, len); 248 memcpy(dest, option, len);
210 dest[len] = '\0'; 249 dest[len] = '\0';
250 if (type == OPTION_STRING_HOST && !good_hostname(dest))
251 safe_strncpy(dest, "bad", len);
211 return ret; 252 return ret;
212 case OPTION_STATIC_ROUTES: { 253 case OPTION_STATIC_ROUTES: {
213 /* Option binary format: 254 /* Option binary format:
@@ -387,6 +428,7 @@ static char **fill_envp(struct dhcp_packet *packet)
387 /* +1 element for each option, +2 for subnet option: */ 428 /* +1 element for each option, +2 for subnet option: */
388 if (packet) { 429 if (packet) {
389 /* note: do not search for "pad" (0) and "end" (255) options */ 430 /* note: do not search for "pad" (0) and "end" (255) options */
431//TODO: change logic to scan packet _once_
390 for (i = 1; i < 255; i++) { 432 for (i = 1; i < 255; i++) {
391 temp = udhcp_get_option(packet, i); 433 temp = udhcp_get_option(packet, i);
392 if (temp) { 434 if (temp) {
@@ -499,9 +541,6 @@ static void udhcp_run_script(struct dhcp_packet *packet, const char *name)
499 char **envp, **curr; 541 char **envp, **curr;
500 char *argv[3]; 542 char *argv[3];
501 543
502 if (client_config.script == NULL)
503 return;
504
505 envp = fill_envp(packet); 544 envp = fill_envp(packet);
506 545
507 /* call script */ 546 /* call script */
@@ -598,6 +637,12 @@ static void add_client_options(struct dhcp_packet *packet)
598// if (client_config.boot_file) 637// if (client_config.boot_file)
599// strncpy((char*)packet->file, client_config.boot_file, sizeof(packet->file) - 1); 638// strncpy((char*)packet->file, client_config.boot_file, sizeof(packet->file) - 1);
600 } 639 }
640
641 // This will be needed if we remove -V VENDOR_STR in favor of
642 // -x vendor:VENDOR_STR
643 //if (!udhcp_find_option(packet.options, DHCP_VENDOR))
644 // /* not set, set the default vendor ID */
645 // ...add (DHCP_VENDOR, "udhcp "BB_VER) opt...
601} 646}
602 647
603/* RFC 2131 648/* RFC 2131
@@ -742,7 +787,7 @@ static NOINLINE int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
742#if ENABLE_FEATURE_UDHCPC_ARPING 787#if ENABLE_FEATURE_UDHCPC_ARPING
743/* Broadcast a DHCP decline message */ 788/* Broadcast a DHCP decline message */
744/* NOINLINE: limit stack usage in caller */ 789/* NOINLINE: limit stack usage in caller */
745static NOINLINE int send_decline(uint32_t xid, uint32_t server, uint32_t requested) 790static NOINLINE int send_decline(/*uint32_t xid,*/ uint32_t server, uint32_t requested)
746{ 791{
747 struct dhcp_packet packet; 792 struct dhcp_packet packet;
748 793
@@ -751,12 +796,14 @@ static NOINLINE int send_decline(uint32_t xid, uint32_t server, uint32_t request
751 */ 796 */
752 init_packet(&packet, DHCPDECLINE); 797 init_packet(&packet, DHCPDECLINE);
753 798
799#if 0
754 /* RFC 2131 says DHCPDECLINE's xid is randomly selected by client, 800 /* RFC 2131 says DHCPDECLINE's xid is randomly selected by client,
755 * but in case the server is buggy and wants DHCPDECLINE's xid 801 * but in case the server is buggy and wants DHCPDECLINE's xid
756 * to match the xid which started entire handshake, 802 * to match the xid which started entire handshake,
757 * we use the same xid we used in initial DHCPDISCOVER: 803 * we use the same xid we used in initial DHCPDISCOVER:
758 */ 804 */
759 packet.xid = xid; 805 packet.xid = xid;
806#endif
760 /* DHCPDECLINE uses "requested ip", not ciaddr, to store offered IP */ 807 /* DHCPDECLINE uses "requested ip", not ciaddr, to store offered IP */
761 udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); 808 udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
762 809
@@ -794,7 +841,6 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
794 struct ip_udp_dhcp_packet packet; 841 struct ip_udp_dhcp_packet packet;
795 uint16_t check; 842 uint16_t check;
796 843
797 memset(&packet, 0, sizeof(packet));
798 bytes = safe_read(fd, &packet, sizeof(packet)); 844 bytes = safe_read(fd, &packet, sizeof(packet));
799 if (bytes < 0) { 845 if (bytes < 0) {
800 log1("Packet read error, ignoring"); 846 log1("Packet read error, ignoring");
@@ -852,7 +898,7 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
852 return -2; 898 return -2;
853 } 899 }
854 900
855 log1("Got valid DHCP packet"); 901 log1("Received a packet");
856 udhcp_dump_packet(&packet.data); 902 udhcp_dump_packet(&packet.data);
857 903
858 bytes -= sizeof(packet.ip) + sizeof(packet.udp); 904 bytes -= sizeof(packet.ip) + sizeof(packet.udp);
@@ -1004,7 +1050,7 @@ static void perform_renew(void)
1004 } 1050 }
1005} 1051}
1006 1052
1007static void perform_release(uint32_t requested_ip, uint32_t server_addr) 1053static void perform_release(uint32_t server_addr, uint32_t requested_ip)
1008{ 1054{
1009 char buffer[sizeof("255.255.255.255")]; 1055 char buffer[sizeof("255.255.255.255")];
1010 struct in_addr temp_addr; 1056 struct in_addr temp_addr;
@@ -1053,7 +1099,7 @@ static void client_background(void)
1053//usage:#endif 1099//usage:#endif
1054//usage:#define udhcpc_trivial_usage 1100//usage:#define udhcpc_trivial_usage
1055//usage: "[-fbnq"IF_UDHCP_VERBOSE("v")"oCRB] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n" 1101//usage: "[-fbnq"IF_UDHCP_VERBOSE("v")"oCRB] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n"
1056//usage: " [-H HOSTNAME] [-V VENDOR] [-x OPT:VAL]... [-O OPT]..." IF_FEATURE_UDHCP_PORT(" [-P N]") 1102//usage: " [-V VENDOR] [-x OPT:VAL]... [-O OPT]..." IF_FEATURE_UDHCP_PORT(" [-P N]")
1057//usage:#define udhcpc_full_usage "\n" 1103//usage:#define udhcpc_full_usage "\n"
1058//usage: IF_LONG_OPTS( 1104//usage: IF_LONG_OPTS(
1059//usage: "\n -i,--interface IFACE Interface to use (default eth0)" 1105//usage: "\n -i,--interface IFACE Interface to use (default eth0)"
@@ -1086,7 +1132,6 @@ static void client_background(void)
1086//usage: "\n -x lease:3600 - option 51 (lease time)" 1132//usage: "\n -x lease:3600 - option 51 (lease time)"
1087//usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)" 1133//usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)"
1088//usage: "\n -F,--fqdn NAME Ask server to update DNS mapping for NAME" 1134//usage: "\n -F,--fqdn NAME Ask server to update DNS mapping for NAME"
1089//usage: "\n -H,-h,--hostname NAME Send NAME as client hostname (default none)"
1090//usage: "\n -V,--vendorclass VENDOR Vendor identifier (default 'udhcp VERSION')" 1135//usage: "\n -V,--vendorclass VENDOR Vendor identifier (default 'udhcp VERSION')"
1091//usage: "\n -C,--clientid-none Don't send MAC as client identifier" 1136//usage: "\n -C,--clientid-none Don't send MAC as client identifier"
1092//usage: IF_UDHCP_VERBOSE( 1137//usage: IF_UDHCP_VERBOSE(
@@ -1124,7 +1169,6 @@ static void client_background(void)
1124//usage: "\n -x lease:3600 - option 51 (lease time)" 1169//usage: "\n -x lease:3600 - option 51 (lease time)"
1125//usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)" 1170//usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)"
1126//usage: "\n -F NAME Ask server to update DNS mapping for NAME" 1171//usage: "\n -F NAME Ask server to update DNS mapping for NAME"
1127//usage: "\n -H,-h NAME Send NAME as client hostname (default none)"
1128//usage: "\n -V VENDOR Vendor identifier (default 'udhcp VERSION')" 1172//usage: "\n -V VENDOR Vendor identifier (default 'udhcp VERSION')"
1129//usage: "\n -C Don't send MAC as client identifier" 1173//usage: "\n -C Don't send MAC as client identifier"
1130//usage: IF_UDHCP_VERBOSE( 1174//usage: IF_UDHCP_VERBOSE(
@@ -1132,8 +1176,8 @@ static void client_background(void)
1132//usage: ) 1176//usage: )
1133//usage: ) 1177//usage: )
1134//usage: "\nSignals:" 1178//usage: "\nSignals:"
1135//usage: "\n USR1 Renew current lease" 1179//usage: "\n USR1 Renew lease"
1136//usage: "\n USR2 Release current lease" 1180//usage: "\n USR2 Release lease"
1137 1181
1138 1182
1139int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1183int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -1150,16 +1194,13 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1150 int discover_retries = 3; 1194 int discover_retries = 3;
1151 uint32_t server_addr = server_addr; /* for compiler */ 1195 uint32_t server_addr = server_addr; /* for compiler */
1152 uint32_t requested_ip = 0; 1196 uint32_t requested_ip = 0;
1153 uint32_t xid = 0; 1197 uint32_t xid = xid; /* for compiler */
1154 uint32_t lease_seconds = 0; /* can be given as 32-bit quantity */
1155 int packet_num; 1198 int packet_num;
1156 int timeout; /* must be signed */ 1199 int timeout; /* must be signed */
1157 unsigned already_waited_sec; 1200 unsigned already_waited_sec;
1158 unsigned opt; 1201 unsigned opt;
1159 int max_fd; 1202 int max_fd;
1160 int retval; 1203 int retval;
1161 struct timeval tv;
1162 struct dhcp_packet packet;
1163 fd_set rfds; 1204 fd_set rfds;
1164 1205
1165 /* Default options */ 1206 /* Default options */
@@ -1186,9 +1227,12 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1186 , &list_x 1227 , &list_x
1187 IF_FEATURE_UDHCP_PORT(, &str_P) 1228 IF_FEATURE_UDHCP_PORT(, &str_P)
1188 IF_UDHCP_VERBOSE(, &dhcp_verbose) 1229 IF_UDHCP_VERBOSE(, &dhcp_verbose)
1189 ); 1230 );
1190 if (opt & (OPT_h|OPT_H)) 1231 if (opt & (OPT_h|OPT_H)) {
1232 //msg added 2011-11
1233 bb_error_msg("option -h NAME is deprecated, use -x hostname:NAME");
1191 client_config.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0); 1234 client_config.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0);
1235 }
1192 if (opt & OPT_F) { 1236 if (opt & OPT_F) {
1193 /* FQDN option format: [0x51][len][flags][0][0]<fqdn> */ 1237 /* FQDN option format: [0x51][len][flags][0][0]<fqdn> */
1194 client_config.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3); 1238 client_config.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3);
@@ -1249,8 +1293,16 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1249 clientid_mac_ptr = client_config.clientid + OPT_DATA+1; 1293 clientid_mac_ptr = client_config.clientid + OPT_DATA+1;
1250 memcpy(clientid_mac_ptr, client_config.client_mac, 6); 1294 memcpy(clientid_mac_ptr, client_config.client_mac, 6);
1251 } 1295 }
1252 if (str_V[0] != '\0') 1296 if (str_V[0] != '\0') {
1297 // can drop -V, str_V, client_config.vendorclass,
1298 // but need to add "vendor" to the list of recognized
1299 // string opts for this to work;
1300 // and need to tweak add_client_options() too...
1301 // ...so the question is, should we?
1302 //bb_error_msg("option -V VENDOR is deprecated, use -x vendor:VENDOR");
1253 client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0); 1303 client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0);
1304 }
1305
1254#if !BB_MMU 1306#if !BB_MMU
1255 /* on NOMMU reexec (i.e., background) early */ 1307 /* on NOMMU reexec (i.e., background) early */
1256 if (!(opt & OPT_f)) { 1308 if (!(opt & OPT_f)) {
@@ -1288,6 +1340,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1288 * "continue" statements in code below jump to the top of the loop. 1340 * "continue" statements in code below jump to the top of the loop.
1289 */ 1341 */
1290 for (;;) { 1342 for (;;) {
1343 struct timeval tv;
1344 struct dhcp_packet packet;
1291 /* silence "uninitialized!" warning */ 1345 /* silence "uninitialized!" warning */
1292 unsigned timestamp_before_wait = timestamp_before_wait; 1346 unsigned timestamp_before_wait = timestamp_before_wait;
1293 1347
@@ -1335,7 +1389,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1335 NULL, 1389 NULL,
1336 client_config.client_mac) 1390 client_config.client_mac)
1337 ) { 1391 ) {
1338 return 1; /* iface is gone? */ 1392 goto ret0; /* iface is gone? */
1339 } 1393 }
1340 if (clientid_mac_ptr) 1394 if (clientid_mac_ptr)
1341 memcpy(clientid_mac_ptr, client_config.client_mac, 6); 1395 memcpy(clientid_mac_ptr, client_config.client_mac, 6);
@@ -1472,13 +1526,11 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1472 timeout = 0; 1526 timeout = 0;
1473 continue; 1527 continue;
1474 case SIGUSR2: 1528 case SIGUSR2:
1475 perform_release(requested_ip, server_addr); 1529 perform_release(server_addr, requested_ip);
1476 timeout = INT_MAX; 1530 timeout = INT_MAX;
1477 continue; 1531 continue;
1478 case SIGTERM: 1532 case SIGTERM:
1479 bb_info_msg("Received SIGTERM"); 1533 bb_info_msg("Received SIGTERM");
1480 if (opt & OPT_R) /* release on quit */
1481 perform_release(requested_ip, server_addr);
1482 goto ret0; 1534 goto ret0;
1483 } 1535 }
1484 1536
@@ -1531,7 +1583,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1531 1583
1532 switch (state) { 1584 switch (state) {
1533 case INIT_SELECTING: 1585 case INIT_SELECTING:
1534 /* Must be a DHCPOFFER to one of our xid's */ 1586 /* Must be a DHCPOFFER */
1535 if (*message == DHCPOFFER) { 1587 if (*message == DHCPOFFER) {
1536/* What exactly is server's IP? There are several values. 1588/* What exactly is server's IP? There are several values.
1537 * Example DHCP offer captured with tchdump: 1589 * Example DHCP offer captured with tchdump:
@@ -1575,6 +1627,9 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1575 case RENEW_REQUESTED: 1627 case RENEW_REQUESTED:
1576 case REBINDING: 1628 case REBINDING:
1577 if (*message == DHCPACK) { 1629 if (*message == DHCPACK) {
1630 uint32_t lease_seconds;
1631 struct in_addr temp_addr;
1632
1578 temp = udhcp_get_option(&packet, DHCP_LEASE_TIME); 1633 temp = udhcp_get_option(&packet, DHCP_LEASE_TIME);
1579 if (!temp) { 1634 if (!temp) {
1580 bb_error_msg("no lease time with ACK, using 1 hour lease"); 1635 bb_error_msg("no lease time with ACK, using 1 hour lease");
@@ -1583,9 +1638,11 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1583 /* it IS unaligned sometimes, don't "optimize" */ 1638 /* it IS unaligned sometimes, don't "optimize" */
1584 move_from_unaligned32(lease_seconds, temp); 1639 move_from_unaligned32(lease_seconds, temp);
1585 lease_seconds = ntohl(lease_seconds); 1640 lease_seconds = ntohl(lease_seconds);
1586 lease_seconds &= 0x0fffffff; /* paranoia: must not be prone to overflows */ 1641 /* paranoia: must not be too small and not prone to overflows */
1587 if (lease_seconds < 10) /* and not too small */ 1642 if (lease_seconds < 0x10)
1588 lease_seconds = 10; 1643 lease_seconds = 0x10;
1644 if (lease_seconds >= 0x10000000)
1645 lease_seconds = 0x0fffffff;
1589 } 1646 }
1590#if ENABLE_FEATURE_UDHCPC_ARPING 1647#if ENABLE_FEATURE_UDHCPC_ARPING
1591 if (opt & OPT_a) { 1648 if (opt & OPT_a) {
@@ -1606,7 +1663,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1606 ) { 1663 ) {
1607 bb_info_msg("Offered address is in use " 1664 bb_info_msg("Offered address is in use "
1608 "(got ARP reply), declining"); 1665 "(got ARP reply), declining");
1609 send_decline(xid, server_addr, packet.yiaddr); 1666 send_decline(/*xid,*/ server_addr, packet.yiaddr);
1610 1667
1611 if (state != REQUESTING) 1668 if (state != REQUESTING)
1612 udhcp_run_script(NULL, "deconfig"); 1669 udhcp_run_script(NULL, "deconfig");
@@ -1623,20 +1680,15 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1623#endif 1680#endif
1624 /* enter bound state */ 1681 /* enter bound state */
1625 timeout = lease_seconds / 2; 1682 timeout = lease_seconds / 2;
1626 { 1683 temp_addr.s_addr = packet.yiaddr;
1627 struct in_addr temp_addr; 1684 bb_info_msg("Lease of %s obtained, lease time %u",
1628 temp_addr.s_addr = packet.yiaddr; 1685 inet_ntoa(temp_addr), (unsigned)lease_seconds);
1629 bb_info_msg("Lease of %s obtained, lease time %u",
1630 inet_ntoa(temp_addr), (unsigned)lease_seconds);
1631 }
1632 requested_ip = packet.yiaddr; 1686 requested_ip = packet.yiaddr;
1633 udhcp_run_script(&packet, state == REQUESTING ? "bound" : "renew"); 1687 udhcp_run_script(&packet, state == REQUESTING ? "bound" : "renew");
1634 1688
1635 state = BOUND; 1689 state = BOUND;
1636 change_listen_mode(LISTEN_NONE); 1690 change_listen_mode(LISTEN_NONE);
1637 if (opt & OPT_q) { /* quit after lease */ 1691 if (opt & OPT_q) { /* quit after lease */
1638 if (opt & OPT_R) /* release on quit */
1639 perform_release(requested_ip, server_addr);
1640 goto ret0; 1692 goto ret0;
1641 } 1693 }
1642 /* future renew failures should not exit (JM) */ 1694 /* future renew failures should not exit (JM) */
@@ -1648,6 +1700,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1648 opt = ((opt & ~OPT_b) | OPT_f); 1700 opt = ((opt & ~OPT_b) | OPT_f);
1649 } 1701 }
1650#endif 1702#endif
1703 /* make future renew packets use different xid */
1704 /* xid = random_xid(); ...but why bother? */
1651 already_waited_sec = 0; 1705 already_waited_sec = 0;
1652 continue; /* back to main loop */ 1706 continue; /* back to main loop */
1653 } 1707 }
@@ -1674,6 +1728,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1674 } /* for (;;) - main loop ends */ 1728 } /* for (;;) - main loop ends */
1675 1729
1676 ret0: 1730 ret0:
1731 if (opt & OPT_R) /* release on quit */
1732 perform_release(server_addr, requested_ip);
1677 retval = 0; 1733 retval = 0;
1678 ret: 1734 ret:
1679 /*if (client_config.pidfile) - remove_pidfile has its own check */ 1735 /*if (client_config.pidfile) - remove_pidfile has its own check */
diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c
index 4d5ff0676..33c9585cf 100644
--- a/networking/udhcp/packet.c
+++ b/networking/udhcp/packet.c
@@ -81,7 +81,6 @@ void FAST_FUNC udhcp_dump_packet(struct dhcp_packet *packet)
81int FAST_FUNC udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd) 81int FAST_FUNC udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd)
82{ 82{
83 int bytes; 83 int bytes;
84 unsigned char *vendor;
85 84
86 memset(packet, 0, sizeof(*packet)); 85 memset(packet, 0, sizeof(*packet));
87 bytes = safe_read(fd, packet, sizeof(*packet)); 86 bytes = safe_read(fd, packet, sizeof(*packet));
@@ -90,42 +89,15 @@ int FAST_FUNC udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd)
90 return bytes; /* returns -1 */ 89 return bytes; /* returns -1 */
91 } 90 }
92 91
93 if (packet->cookie != htonl(DHCP_MAGIC)) { 92 if (bytes < offsetof(struct dhcp_packet, options)
93 || packet->cookie != htonl(DHCP_MAGIC)
94 ) {
94 bb_info_msg("Packet with bad magic, ignoring"); 95 bb_info_msg("Packet with bad magic, ignoring");
95 return -2; 96 return -2;
96 } 97 }
97 log1("Received a packet"); 98 log1("Received a packet");
98 udhcp_dump_packet(packet); 99 udhcp_dump_packet(packet);
99 100
100 if (packet->op == BOOTREQUEST) {
101 vendor = udhcp_get_option(packet, DHCP_VENDOR);
102 if (vendor) {
103#if 0
104 static const char broken_vendors[][8] = {
105 "MSFT 98",
106 ""
107 };
108 int i;
109 for (i = 0; broken_vendors[i][0]; i++) {
110 if (vendor[OPT_LEN - OPT_DATA] == (uint8_t)strlen(broken_vendors[i])
111 && strncmp((char*)vendor, broken_vendors[i], vendor[OPT_LEN - OPT_DATA]) == 0
112 ) {
113 log1("Broken client (%s), forcing broadcast replies",
114 broken_vendors[i]);
115 packet->flags |= htons(BROADCAST_FLAG);
116 }
117 }
118#else
119 if (vendor[OPT_LEN - OPT_DATA] == (uint8_t)(sizeof("MSFT 98")-1)
120 && memcmp(vendor, "MSFT 98", sizeof("MSFT 98")-1) == 0
121 ) {
122 log1("Broken client (%s), forcing broadcast replies", "MSFT 98");
123 packet->flags |= htons(BROADCAST_FLAG);
124 }
125#endif
126 }
127 }
128
129 return bytes; 101 return bytes;
130} 102}
131 103
@@ -210,7 +182,7 @@ int FAST_FUNC udhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
210 uint32_t source_nip, int source_port, 182 uint32_t source_nip, int source_port,
211 uint32_t dest_nip, int dest_port) 183 uint32_t dest_nip, int dest_port)
212{ 184{
213 struct sockaddr_in client; 185 struct sockaddr_in sa;
214 unsigned padding; 186 unsigned padding;
215 int fd; 187 int fd;
216 int result = -1; 188 int result = -1;
@@ -223,26 +195,25 @@ int FAST_FUNC udhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
223 } 195 }
224 setsockopt_reuseaddr(fd); 196 setsockopt_reuseaddr(fd);
225 197
226 memset(&client, 0, sizeof(client)); 198 memset(&sa, 0, sizeof(sa));
227 client.sin_family = AF_INET; 199 sa.sin_family = AF_INET;
228 client.sin_port = htons(source_port); 200 sa.sin_port = htons(source_port);
229 client.sin_addr.s_addr = source_nip; 201 sa.sin_addr.s_addr = source_nip;
230 if (bind(fd, (struct sockaddr *)&client, sizeof(client)) == -1) { 202 if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
231 msg = "bind(%s)"; 203 msg = "bind(%s)";
232 goto ret_close; 204 goto ret_close;
233 } 205 }
234 206
235 memset(&client, 0, sizeof(client)); 207 memset(&sa, 0, sizeof(sa));
236 client.sin_family = AF_INET; 208 sa.sin_family = AF_INET;
237 client.sin_port = htons(dest_port); 209 sa.sin_port = htons(dest_port);
238 client.sin_addr.s_addr = dest_nip; 210 sa.sin_addr.s_addr = dest_nip;
239 if (connect(fd, (struct sockaddr *)&client, sizeof(client)) == -1) { 211 if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
240 msg = "connect"; 212 msg = "connect";
241 goto ret_close; 213 goto ret_close;
242 } 214 }
243 215
244 udhcp_dump_packet(dhcp_pkt); 216 udhcp_dump_packet(dhcp_pkt);
245
246 padding = DHCP_OPTIONS_BUFSIZE - 1 - udhcp_end_option(dhcp_pkt->options); 217 padding = DHCP_OPTIONS_BUFSIZE - 1 - udhcp_end_option(dhcp_pkt->options);
247 result = safe_write(fd, dhcp_pkt, DHCP_SIZE - padding); 218 result = safe_write(fd, dhcp_pkt, DHCP_SIZE - padding);
248 msg = "write"; 219 msg = "write";
diff --git a/networking/vconfig.c b/networking/vconfig.c
index 48b45d9af..924b2f009 100644
--- a/networking/vconfig.c
+++ b/networking/vconfig.c
@@ -13,12 +13,12 @@
13//usage: "COMMAND [OPTIONS]" 13//usage: "COMMAND [OPTIONS]"
14//usage:#define vconfig_full_usage "\n\n" 14//usage:#define vconfig_full_usage "\n\n"
15//usage: "Create and remove virtual ethernet devices\n" 15//usage: "Create and remove virtual ethernet devices\n"
16//usage: "\n add [interface-name] [vlan_id]" 16//usage: "\n add IFACE VLAN_ID"
17//usage: "\n rem [vlan-name]" 17//usage: "\n rem VLAN_NAME"
18//usage: "\n set_flag [interface-name] [flag-num] [0 | 1]" 18//usage: "\n set_flag IFACE 0|1 VLAN_QOS"
19//usage: "\n set_egress_map [vlan-name] [skb_priority] [vlan_qos]" 19//usage: "\n set_egress_map VLAN_NAME SKB_PRIO VLAN_QOS"
20//usage: "\n set_ingress_map [vlan-name] [skb_priority] [vlan_qos]" 20//usage: "\n set_ingress_map VLAN_NAME SKB_PRIO VLAN_QOS"
21//usage: "\n set_name_type [name-type]" 21//usage: "\n set_name_type NAME_TYPE"
22 22
23#include "libbb.h" 23#include "libbb.h"
24#include <net/if.h> 24#include <net/if.h>
@@ -66,58 +66,40 @@ struct vlan_ioctl_args {
66 * The return value is the last data entry for the matching string. */ 66 * The return value is the last data entry for the matching string. */
67static const char *xfind_str(const char *table, const char *str) 67static const char *xfind_str(const char *table, const char *str)
68{ 68{
69 while (strcasecmp(str, table+1) != 0) { 69 while (strcasecmp(str, table + 1) != 0) {
70 table += table[0]; 70 if (!table[0])
71 if (!*table) {
72 bb_show_usage(); 71 bb_show_usage();
73 } 72 table += table[0];
74 } 73 }
75 return table - 1; 74 return table - 1;
76} 75}
77 76
78static const char cmds[] ALIGN1 = { 77static const char cmds[] ALIGN1 = {
79 4, ADD_VLAN_CMD, 7, 78 4, ADD_VLAN_CMD, 7,
80 'a', 'd', 'd', 0, 79 'a','d','d',0,
81 3, DEL_VLAN_CMD, 7, 80 3, DEL_VLAN_CMD, 7,
82 'r', 'e', 'm', 0, 81 'r','e','m',0,
83 3, SET_VLAN_NAME_TYPE_CMD, 17, 82 3, SET_VLAN_NAME_TYPE_CMD, 17,
84 's', 'e', 't', '_', 83 's','e','t','_','n','a','m','e','_','t','y','p','e',0,
85 'n', 'a', 'm', 'e', '_',
86 't', 'y', 'p', 'e', 0,
87 5, SET_VLAN_FLAG_CMD, 12, 84 5, SET_VLAN_FLAG_CMD, 12,
88 's', 'e', 't', '_', 85 's','e','t','_','f','l','a','g',0,
89 'f', 'l', 'a', 'g', 0,
90 5, SET_VLAN_EGRESS_PRIORITY_CMD, 18, 86 5, SET_VLAN_EGRESS_PRIORITY_CMD, 18,
91 's', 'e', 't', '_', 87 's','e','t','_','e','g','r','e','s','s','_','m','a','p',0,
92 'e', 'g', 'r', 'e', 's', 's', '_', 88 5, SET_VLAN_INGRESS_PRIORITY_CMD, 0,
93 'm', 'a', 'p', 0, 89 's','e','t','_','i','n','g','r','e','s','s','_','m','a','p',0,
94 5, SET_VLAN_INGRESS_PRIORITY_CMD, 16,
95 's', 'e', 't', '_',
96 'i', 'n', 'g', 'r', 'e', 's', 's', '_',
97 'm', 'a', 'p', 0,
98}; 90};
99 91
100static const char name_types[] ALIGN1 = { 92static const char name_types[] ALIGN1 = {
101 VLAN_NAME_TYPE_PLUS_VID, 16, 93 VLAN_NAME_TYPE_PLUS_VID, 16,
102 'V', 'L', 'A', 'N', 94 'V','L','A','N','_','P','L','U','S','_','V','I','D',0,
103 '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
104 0,
105 VLAN_NAME_TYPE_PLUS_VID_NO_PAD, 22, 95 VLAN_NAME_TYPE_PLUS_VID_NO_PAD, 22,
106 'V', 'L', 'A', 'N', 96 'V','L','A','N','_','P','L','U','S','_','V','I','D','_','N','O','_','P','A','D',0,
107 '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
108 '_', 'N', 'O', '_', 'P', 'A', 'D', 0,
109 VLAN_NAME_TYPE_RAW_PLUS_VID, 15, 97 VLAN_NAME_TYPE_RAW_PLUS_VID, 15,
110 'D', 'E', 'V', 98 'D','E','V','_','P','L','U','S','_','V','I','D',0,
111 '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D', 99 VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, 0,
112 0, 100 'D','E','V','_','P','L','U','S','_','V','I','D','_','N','O','_','P','A','D',0,
113 VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, 20,
114 'D', 'E', 'V',
115 '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
116 '_', 'N', 'O', '_', 'P', 'A', 'D', 0,
117}; 101};
118 102
119static const char conf_file_name[] ALIGN1 = "/proc/net/vlan/config";
120
121int vconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 103int vconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
122int vconfig_main(int argc, char **argv) 104int vconfig_main(int argc, char **argv)
123{ 105{
@@ -125,25 +107,19 @@ int vconfig_main(int argc, char **argv)
125 const char *p; 107 const char *p;
126 int fd; 108 int fd;
127 109
128 if (argc < 3) {
129 bb_show_usage();
130 }
131
132 /* Don't bother closing the filedes. It will be closed on cleanup. */
133 /* Will die if 802.1q is not present */
134 xopen(conf_file_name, O_RDONLY);
135
136 memset(&ifr, 0, sizeof(ifr)); 110 memset(&ifr, 0, sizeof(ifr));
137 111
138 ++argv; 112 ++argv;
139 p = xfind_str(cmds+2, *argv); 113 if (!argv[0])
114 bb_show_usage();
115 p = xfind_str(cmds + 2, argv[0]);
140 ifr.cmd = *p; 116 ifr.cmd = *p;
141 if (argc != p[-1]) { 117 if (argc != p[-1])
142 bb_show_usage(); 118 bb_show_usage();
143 }
144 119
145 if (ifr.cmd == SET_VLAN_NAME_TYPE_CMD) { /* set_name_type */ 120 if (ifr.cmd == SET_VLAN_NAME_TYPE_CMD) {
146 ifr.u.name_type = *xfind_str(name_types+1, argv[1]); 121 /* set_name_type */
122 ifr.u.name_type = *xfind_str(name_types + 1, argv[1]);
147 } else { 123 } else {
148 strncpy_IFNAMSIZ(ifr.device1, argv[1]); 124 strncpy_IFNAMSIZ(ifr.device1, argv[1]);
149 p = argv[2]; 125 p = argv[2];
@@ -152,22 +128,26 @@ int vconfig_main(int argc, char **argv)
152 * since ifr.u.flag, ifr.u.VID, and ifr.u.skb_priority are all same-sized 128 * since ifr.u.flag, ifr.u.VID, and ifr.u.skb_priority are all same-sized
153 * (unsigned) int members of a unions. But because of the range checking, 129 * (unsigned) int members of a unions. But because of the range checking,
154 * doing so wouldn't save that much space and would also make maintainence 130 * doing so wouldn't save that much space and would also make maintainence
155 * more of a pain. */ 131 * more of a pain.
156 if (ifr.cmd == SET_VLAN_FLAG_CMD) { /* set_flag */ 132 */
157 ifr.u.flag = xatoul_range(p, 0, 1); 133 if (ifr.cmd == SET_VLAN_FLAG_CMD) {
134 /* set_flag */
135 ifr.u.flag = xatou_range(p, 0, 1);
158 /* DM: in order to set reorder header, qos must be set */ 136 /* DM: in order to set reorder header, qos must be set */
159 ifr.vlan_qos = xatoul_range(argv[3], 0, 7); 137 ifr.vlan_qos = xatou_range(argv[3], 0, 7);
160 } else if (ifr.cmd == ADD_VLAN_CMD) { /* add */ 138 } else if (ifr.cmd == ADD_VLAN_CMD) {
161 ifr.u.VID = xatoul_range(p, 0, VLAN_GROUP_ARRAY_LEN-1); 139 /* add */
162 } else if (ifr.cmd != DEL_VLAN_CMD) { /* set_{egress|ingress}_map */ 140 ifr.u.VID = xatou_range(p, 0, VLAN_GROUP_ARRAY_LEN - 1);
141 } else if (ifr.cmd != DEL_VLAN_CMD) {
142 /* set_{egress|ingress}_map */
163 ifr.u.skb_priority = xatou(p); 143 ifr.u.skb_priority = xatou(p);
164 ifr.vlan_qos = xatoul_range(argv[3], 0, 7); 144 ifr.vlan_qos = xatou_range(argv[3], 0, 7);
165 } 145 }
166 } 146 }
167 147
168 fd = xsocket(AF_INET, SOCK_STREAM, 0); 148 fd = xsocket(AF_INET, SOCK_STREAM, 0);
169 ioctl_or_perror_and_die(fd, SIOCSIFVLAN, &ifr, 149 ioctl_or_perror_and_die(fd, SIOCSIFVLAN, &ifr,
170 "ioctl error for %s", *argv); 150 "ioctl error for %s", argv[0]);
171 151
172 return 0; 152 return 0;
173} 153}
diff --git a/networking/wget.c b/networking/wget.c
index 4dd42de9d..ea46d1795 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -557,6 +557,7 @@ static void download_one_url(const char *url)
557 FILE *dfp; /* socket to ftp server (data) */ 557 FILE *dfp; /* socket to ftp server (data) */
558 char *proxy = NULL; 558 char *proxy = NULL;
559 char *fname_out_alloc; 559 char *fname_out_alloc;
560 char *redirected_path = NULL;
560 struct host_info server; 561 struct host_info server;
561 struct host_info target; 562 struct host_info target;
562 563
@@ -793,8 +794,8 @@ However, in real world it was observed that some web servers
793 bb_error_msg_and_die("too many redirections"); 794 bb_error_msg_and_die("too many redirections");
794 fclose(sfp); 795 fclose(sfp);
795 if (str[0] == '/') { 796 if (str[0] == '/') {
796 free(target.allocated); 797 free(redirected_path);
797 target.path = target.allocated = xstrdup(str+1); 798 target.path = redirected_path = xstrdup(str+1);
798 /* lsa stays the same: it's on the same server */ 799 /* lsa stays the same: it's on the same server */
799 } else { 800 } else {
800 parse_url(str, &target); 801 parse_url(str, &target);
@@ -849,6 +850,7 @@ However, in real world it was observed that some web servers
849 free(server.allocated); 850 free(server.allocated);
850 free(target.allocated); 851 free(target.allocated);
851 free(fname_out_alloc); 852 free(fname_out_alloc);
853 free(redirected_path);
852} 854}
853 855
854int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 856int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;