diff options
author | Ron Yorston <rmy@pobox.com> | 2012-03-23 12:12:03 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2012-03-23 12:12:03 +0000 |
commit | b0f54743e36af163ae2530c381c485bb29df13dc (patch) | |
tree | cda4cfeaae6e47fe4f14c1b566092be4da9affc4 /networking | |
parent | 40514a0309939f2446f0d4ed9600cad5de396e7f (diff) | |
parent | ba88826c66411affc1da3614742b454654f7298a (diff) | |
download | busybox-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.src | 16 | ||||
-rw-r--r-- | networking/ftpd.c | 3 | ||||
-rw-r--r-- | networking/ftpgetput.c | 34 | ||||
-rw-r--r-- | networking/httpd.c | 276 | ||||
-rw-r--r-- | networking/httpd_indexcgi.c | 27 | ||||
-rw-r--r-- | networking/ifupdown.c | 14 | ||||
-rw-r--r-- | networking/ip.c | 13 | ||||
-rw-r--r-- | networking/nc_bloaty.c | 43 | ||||
-rw-r--r-- | networking/ntpd.c | 281 | ||||
-rw-r--r-- | networking/tftp.c | 5 | ||||
-rw-r--r-- | networking/udhcp/common.c | 88 | ||||
-rw-r--r-- | networking/udhcp/common.h | 6 | ||||
-rw-r--r-- | networking/udhcp/d6_common.h | 123 | ||||
-rw-r--r-- | networking/udhcp/d6_dhcpc.c | 1483 | ||||
-rw-r--r-- | networking/udhcp/d6_packet.c | 172 | ||||
-rw-r--r-- | networking/udhcp/d6_socket.c | 34 | ||||
-rw-r--r-- | networking/udhcp/dhcpc.c | 168 | ||||
-rw-r--r-- | networking/udhcp/packet.c | 57 | ||||
-rw-r--r-- | networking/vconfig.c | 102 | ||||
-rw-r--r-- | networking/wget.c | 6 |
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 | ||
203 | config FEATURE_HTTPD_AUTH_MD5 | 205 | config 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 | ||
211 | config FEATURE_HTTPD_CGI | 219 | config 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 | ||
230 | config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV | 238 | config 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 |
340 | enum { | 350 | enum { |
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 | */ |
1262 | static void send_cgi_and_exit( | 1274 | static 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; |
1268 | static void send_cgi_and_exit( | 1281 | static 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 | ||
1680 | struct pam_userinfo { | ||
1681 | const char *name; | ||
1682 | const char *pw; | ||
1683 | }; | ||
1684 | |||
1685 | static 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 | */ |
1670 | static int check_user_passwd(const char *path, const char *user_and_passwd) | 1737 | static 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! */ |
161 | enum { | 161 | enum { |
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); | |||
263 | static int doexec(char **proggie) NORETURN; | 262 | static int doexec(char **proggie) NORETURN; |
264 | static int doexec(char **proggie) | 263 | static 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 | ||
210 | typedef struct { | 214 | typedef 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 | ||
261 | struct globals { | 265 | struct 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 | |||
500 | filter_datapoints(peer_t *p) | 517 | filter_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( | |||
434 | int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg) | 436 | int 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 */ | ||
567 | int 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 */ | ||
315 | int sprint_nip6(char *dest, /*const char *pre,*/ const uint8_t *ip) FAST_FUNC; | ||
316 | |||
311 | POP_SAVED_FUNCTION_VISIBILITY | 317 | POP_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 | |||
12 | PUSH_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 | |||
32 | struct 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 | |||
43 | struct ip6_udp_d6_packet { | ||
44 | struct ip6_hdr ip6; | ||
45 | struct udphdr udp; | ||
46 | struct d6_packet data; | ||
47 | } PACKED; | ||
48 | |||
49 | struct udp_d6_packet { | ||
50 | struct udphdr udp; | ||
51 | struct d6_packet data; | ||
52 | } PACKED; | ||
53 | |||
54 | /*** Options ***/ | ||
55 | |||
56 | struct 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 | |||
89 | struct 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 | |||
98 | int FAST_FUNC d6_listen_socket(int port, const char *inf); | ||
99 | |||
100 | int FAST_FUNC d6_recv_kernel_packet( | ||
101 | struct in6_addr *peer_ipv6, | ||
102 | struct d6_packet *packet, int fd | ||
103 | ); | ||
104 | |||
105 | int 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 | |||
112 | int 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 | |||
118 | void FAST_FUNC d6_dump_packet(struct d6_packet *packet); | ||
119 | |||
120 | |||
121 | POP_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 | ||
41 | static 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 */ | ||
62 | enum { | ||
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 | |||
91 | static 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 | |||
115 | static 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 | |||
123 | static 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 | |||
132 | static 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 */ | ||
139 | static 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 | |||
225 | static 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 */ | ||
245 | static 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 | |||
269 | static 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 */ | ||
276 | static 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 | |||
289 | static 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 | |||
304 | static 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 */ | ||
401 | static 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 */ | ||
465 | static 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 */ | ||
534 | static 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 */ | ||
563 | static 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 */ | ||
585 | static 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 | |||
647 | static int sockfd = -1; | ||
648 | |||
649 | #define LISTEN_NONE 0 | ||
650 | #define LISTEN_KERNEL 1 | ||
651 | #define LISTEN_RAW 2 | ||
652 | static 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 | ||
668 | static smallint state; | ||
669 | |||
670 | static 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 | |||
749 | static 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 */ | ||
770 | static 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 | |||
792 | static 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 | ||
818 | static 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 | |||
909 | int udhcpc6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
910 | int 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 | ||
15 | void 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 | |||
29 | int 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 */ | ||
53 | int 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 */ | ||
127 | int 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 | |||
11 | int 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 | ||
127 | static 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 */ |
146 | static int mton(uint32_t mask) | 127 | static 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 | */ | ||
149 | static 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 */ | ||
179 | static 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 */ |
158 | static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_optflag *optflag, const char *opt_name) | 196 | static 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 */ |
745 | static NOINLINE int send_decline(uint32_t xid, uint32_t server, uint32_t requested) | 790 | static 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 | ||
1007 | static void perform_release(uint32_t requested_ip, uint32_t server_addr) | 1053 | static 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 | ||
1139 | int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1183 | int 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) | |||
81 | int FAST_FUNC udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd) | 81 | int 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. */ |
67 | static const char *xfind_str(const char *table, const char *str) | 67 | static 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 | ||
78 | static const char cmds[] ALIGN1 = { | 77 | static 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 | ||
100 | static const char name_types[] ALIGN1 = { | 92 | static 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 | ||
119 | static const char conf_file_name[] ALIGN1 = "/proc/net/vlan/config"; | ||
120 | |||
121 | int vconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 103 | int vconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
122 | int vconfig_main(int argc, char **argv) | 104 | int 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 | ||
854 | int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 856 | int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |