aboutsummaryrefslogtreecommitdiffstats
path: root/package/busybox/patches
diff options
context:
space:
mode:
authorblogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73>2012-10-05 10:12:53 +0000
committerblogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73>2012-10-05 10:12:53 +0000
commit5c105d9f3fd086aff195d3849dcf847d6b0bd927 (patch)
tree1229a11f725bfa58aa7c57a76898553bb5f6654a /package/busybox/patches
downloadopenwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.tar.gz
openwrt-5c105d9f3fd086aff195d3849dcf847d6b0bd927.zip
branch Attitude Adjustment
git-svn-id: svn://svn.openwrt.org/openwrt/branches/attitude_adjustment@33625 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'package/busybox/patches')
-rw-r--r--package/busybox/patches/001-init_avoid_loop_opening_tty.patch15
-rw-r--r--package/busybox/patches/002-passwd_use_md5_hash_by_default_like_it_used_to_be.patch20
-rw-r--r--package/busybox/patches/003-brctl_show_fix.patch37
-rw-r--r--package/busybox/patches/004-upstream-percent_decode_in_place.patch237
-rw-r--r--package/busybox/patches/005-resource_h_include.patch10
-rw-r--r--package/busybox/patches/006-upstream_CVE-2011-2716_fixes.patch164
-rw-r--r--package/busybox/patches/110-wget_getopt_fix.patch11
-rw-r--r--package/busybox/patches/140-trylink_bash.patch8
-rw-r--r--package/busybox/patches/141-gen_build_files_bash.patch8
-rw-r--r--package/busybox/patches/150-no_static_libgcc.patch11
-rw-r--r--package/busybox/patches/200-etc_crontabs.patch22
-rw-r--r--package/busybox/patches/240-udhcpc_retries.patch20
-rw-r--r--package/busybox/patches/241-udhcpc-oversized_packets.patch99
-rw-r--r--package/busybox/patches/242-udhcpc_msgs.patch18
-rw-r--r--package/busybox/patches/243-udhcpc_changed_ifindex.patch15
-rw-r--r--package/busybox/patches/244-udhcpc_add_6rd_option.patch143
-rw-r--r--package/busybox/patches/250-ash_export-n.patch34
-rw-r--r--package/busybox/patches/251-ash_fix-redir-substitution.patch12
-rw-r--r--package/busybox/patches/300-netmsg.patch103
-rw-r--r--package/busybox/patches/310-passwd_access.patch41
-rw-r--r--package/busybox/patches/340-lock_util.patch172
-rw-r--r--package/busybox/patches/350-httpd_redir.patch90
-rw-r--r--package/busybox/patches/410-httpd_cgi_headers.patch16
-rw-r--r--package/busybox/patches/440-httpd_chdir.patch10
-rw-r--r--package/busybox/patches/470-insmod_search.patch137
-rw-r--r--package/busybox/patches/480-mount_union.patch29
-rw-r--r--package/busybox/patches/490-mount_disable_check.patch12
-rw-r--r--package/busybox/patches/510-awk_include.patch217
-rw-r--r--package/busybox/patches/524-udhcpc_renew.patch10
-rw-r--r--package/busybox/patches/610-ntpd_delayed_resolve.patch48
-rw-r--r--package/busybox/patches/700-hexdump_segfault_fix.patch11
-rw-r--r--package/busybox/patches/802-brctl_linux24.patch74
-rw-r--r--package/busybox/patches/803-id_getgrouplist.patch49
-rw-r--r--package/busybox/patches/902-telnetd_intr.patch10
-rw-r--r--package/busybox/patches/910-insmod-q-flag.patch33
-rw-r--r--package/busybox/patches/911-date-k-flag.patch83
-rw-r--r--package/busybox/patches/920-portability.patch13
-rw-r--r--package/busybox/patches/950-partial-checksum.patch87
38 files changed, 2129 insertions, 0 deletions
diff --git a/package/busybox/patches/001-init_avoid_loop_opening_tty.patch b/package/busybox/patches/001-init_avoid_loop_opening_tty.patch
new file mode 100644
index 000000000..4db64c34b
--- /dev/null
+++ b/package/busybox/patches/001-init_avoid_loop_opening_tty.patch
@@ -0,0 +1,15 @@
+--- a/init/init.c
++++ b/init/init.c
+@@ -573,8 +573,11 @@ static void run_actions(int action_type)
+ /* Only run stuff with pid == 0. If pid != 0,
+ * it is already running
+ */
+- if (a->pid == 0)
++ if (a->pid == 0) {
++ if (a->terminal[0] && access(a->terminal, R_OK | W_OK))
++ continue;
+ a->pid = run(a);
++ }
+ }
+ }
+ }
diff --git a/package/busybox/patches/002-passwd_use_md5_hash_by_default_like_it_used_to_be.patch b/package/busybox/patches/002-passwd_use_md5_hash_by_default_like_it_used_to_be.patch
new file mode 100644
index 000000000..ff79f3ee9
--- /dev/null
+++ b/package/busybox/patches/002-passwd_use_md5_hash_by_default_like_it_used_to_be.patch
@@ -0,0 +1,20 @@
+From eb80c2a5315ed08bd329448217695375d89732c9 Mon Sep 17 00:00:00 2001
+From: Nicolas Thill <nico@openwrt.org>
+Date: Wed, 9 Nov 2011 18:17:20 +0100
+Subject: [PATCH] passwd: use MD5 hash by default (like it used to be)
+
+---
+ loginutils/passwd.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+--- a/loginutils/passwd.c
++++ b/loginutils/passwd.c
+@@ -94,7 +94,7 @@ int passwd_main(int argc UNUSED_PARAM, c
+ };
+ unsigned opt;
+ int rc;
+- const char *opt_a = "d"; /* des */
++ const char *opt_a = "m"; /* md5 */
+ const char *filename;
+ char *myname;
+ char *name;
diff --git a/package/busybox/patches/003-brctl_show_fix.patch b/package/busybox/patches/003-brctl_show_fix.patch
new file mode 100644
index 000000000..8177fb01c
--- /dev/null
+++ b/package/busybox/patches/003-brctl_show_fix.patch
@@ -0,0 +1,37 @@
+--- a/networking/brctl.c
++++ b/networking/brctl.c
+@@ -129,7 +129,7 @@ int brctl_main(int argc UNUSED_PARAM, ch
+ "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
+ "setpathcost\0" "setportprio\0" "setbridgeprio\0"
+ )
+- IF_FEATURE_BRCTL_SHOW("showmacs\0" "show\0");
++ IF_FEATURE_BRCTL_SHOW("show\0");
+
+ enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
+ IF_FEATURE_BRCTL_FANCY(,
+@@ -137,7 +137,7 @@ int brctl_main(int argc UNUSED_PARAM, ch
+ ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
+ ARG_setpathcost, ARG_setportprio, ARG_setbridgeprio
+ )
+- IF_FEATURE_BRCTL_SHOW(, ARG_showmacs, ARG_show)
++ IF_FEATURE_BRCTL_SHOW(, ARG_show)
+ };
+
+ int fd;
+--- a/networking/Config.src
++++ b/networking/Config.src
+@@ -82,12 +82,12 @@ config FEATURE_BRCTL_FANCY
+ This adds about 600 bytes.
+
+ config FEATURE_BRCTL_SHOW
+- bool "Support show, showmac and showstp"
++ bool "Support show"
+ default y
+ depends on BRCTL && FEATURE_BRCTL_FANCY
+ help
+ Add support for option which prints the current config:
+- showmacs, showstp, show
++ show
+
+ config DNSD
+ bool "dnsd"
diff --git a/package/busybox/patches/004-upstream-percent_decode_in_place.patch b/package/busybox/patches/004-upstream-percent_decode_in_place.patch
new file mode 100644
index 000000000..d94ee4790
--- /dev/null
+++ b/package/busybox/patches/004-upstream-percent_decode_in_place.patch
@@ -0,0 +1,237 @@
+http://git.busybox.net/busybox/commit/?id=dd1061b6a79b0161597799e825bfefc27993ace5
+
+From dd1061b6a79b0161597799e825bfefc27993ace5 Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux@googlemail.com>
+Date: Sun, 11 Sep 2011 21:04:02 +0200
+Subject: [PATCH] wget: URL-decode user:password before base64-encoding it into auth hdr. Closes 3625.
+
+function old new delta
+percent_decode_in_place - 152 +152
+parse_url 304 317 +13
+handle_incoming_and_exit 2795 2798 +3
+httpd_main 763 760 -3
+decodeString 152 - -152
+------------------------------------------------------------------------------
+(add/remove: 2/1 grow/shrink: 2/1 up/down: 168/-155) Total: 13 bytes
+
+Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
+
+--- a/include/libbb.h
++++ b/include/libbb.h
+@@ -1570,6 +1570,15 @@ int starts_with_cpu(const char *str) FAS
+ unsigned get_cpu_count(void) FAST_FUNC;
+
+
++/* Use strict=1 if you process input from untrusted source:
++ * it will return NULL on invalid %xx (bad hex chars)
++ * and str + 1 if decoded char is / or NUL.
++ * In non-strict mode, it always succeeds (returns str),
++ * and also it additionally decoded '+' to space.
++ */
++char *percent_decode_in_place(char *str, int strict) FAST_FUNC;
++
++
+ extern const char bb_uuenc_tbl_base64[];
+ extern const char bb_uuenc_tbl_std[];
+ void bb_uuencode(char *store, const void *s, int length, const char *tbl) FAST_FUNC;
+--- /dev/null
++++ b/libbb/percent_decode.c
+@@ -0,0 +1,69 @@
++/* vi: set sw=4 ts=4: */
++/*
++ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
++ */
++
++//kbuild:lib-y += percent_decode.o
++
++#include "libbb.h"
++
++static unsigned hex_to_bin(unsigned char c)
++{
++ unsigned v;
++
++ v = c - '0';
++ if (v <= 9)
++ return v;
++ /* c | 0x20: letters to lower case, non-letters
++ * to (potentially different) non-letters */
++ v = (unsigned)(c | 0x20) - 'a';
++ if (v <= 5)
++ return v + 10;
++ return ~0;
++/* For testing:
++void t(char c) { printf("'%c'(%u) %u\n", c, c, hex_to_bin(c)); }
++int main() { t(0x10); t(0x20); t('0'); t('9'); t('A'); t('F'); t('a'); t('f');
++t('0'-1); t('9'+1); t('A'-1); t('F'+1); t('a'-1); t('f'+1); return 0; }
++*/
++}
++
++char* FAST_FUNC percent_decode_in_place(char *str, int strict)
++{
++ /* note that decoded string is always shorter than original */
++ char *src = str;
++ char *dst = str;
++ char c;
++
++ while ((c = *src++) != '\0') {
++ unsigned v;
++
++ if (!strict && c == '+') {
++ *dst++ = ' ';
++ continue;
++ }
++ if (c != '%') {
++ *dst++ = c;
++ continue;
++ }
++ v = hex_to_bin(src[0]);
++ if (v > 15) {
++ bad_hex:
++ if (strict)
++ return NULL;
++ *dst++ = '%';
++ continue;
++ }
++ v = (v * 16) | hex_to_bin(src[1]);
++ if (v > 255)
++ goto bad_hex;
++ if (strict && (v == '/' || v == '\0')) {
++ /* caller takes it as indication of invalid
++ * (dangerous wrt exploits) chars */
++ return str + 1;
++ }
++ *dst++ = v;
++ src += 2;
++ }
++ *dst = '\0';
++ return str;
++}
+--- a/networking/httpd.c
++++ b/networking/httpd.c
+@@ -820,78 +820,6 @@ static char *encodeString(const char *st
+ }
+ #endif
+
+-/*
+- * Given a URL encoded string, convert it to plain ascii.
+- * Since decoding always makes strings smaller, the decode is done in-place.
+- * Thus, callers should xstrdup() the argument if they do not want the
+- * argument modified. The return is the original pointer, allowing this
+- * function to be easily used as arguments to other functions.
+- *
+- * string The first string to decode.
+- * option_d 1 if called for httpd -d
+- *
+- * Returns a pointer to the decoded string (same as input).
+- */
+-static unsigned hex_to_bin(unsigned char c)
+-{
+- unsigned v;
+-
+- v = c - '0';
+- if (v <= 9)
+- return v;
+- /* c | 0x20: letters to lower case, non-letters
+- * to (potentially different) non-letters */
+- v = (unsigned)(c | 0x20) - 'a';
+- if (v <= 5)
+- return v + 10;
+- return ~0;
+-/* For testing:
+-void t(char c) { printf("'%c'(%u) %u\n", c, c, hex_to_bin(c)); }
+-int main() { t(0x10); t(0x20); t('0'); t('9'); t('A'); t('F'); t('a'); t('f');
+-t('0'-1); t('9'+1); t('A'-1); t('F'+1); t('a'-1); t('f'+1); return 0; }
+-*/
+-}
+-static char *decodeString(char *orig, int option_d)
+-{
+- /* note that decoded string is always shorter than original */
+- char *string = orig;
+- char *ptr = string;
+- char c;
+-
+- while ((c = *ptr++) != '\0') {
+- unsigned v;
+-
+- if (option_d && c == '+') {
+- *string++ = ' ';
+- continue;
+- }
+- if (c != '%') {
+- *string++ = c;
+- continue;
+- }
+- v = hex_to_bin(ptr[0]);
+- if (v > 15) {
+- bad_hex:
+- if (!option_d)
+- return NULL;
+- *string++ = '%';
+- continue;
+- }
+- v = (v * 16) | hex_to_bin(ptr[1]);
+- if (v > 255)
+- goto bad_hex;
+- if (!option_d && (v == '/' || v == '\0')) {
+- /* caller takes it as indication of invalid
+- * (dangerous wrt exploits) chars */
+- return orig + 1;
+- }
+- *string++ = v;
+- ptr += 2;
+- }
+- *string = '\0';
+- return orig;
+-}
+-
+ #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ /*
+ * Decode a base64 data stream as per rfc1521.
+@@ -1949,7 +1877,7 @@ static void handle_incoming_and_exit(con
+ }
+
+ /* Decode URL escape sequences */
+- tptr = decodeString(urlcopy, 0);
++ tptr = percent_decode_in_place(urlcopy, /*strict:*/ 1);
+ if (tptr == NULL)
+ send_headers_and_exit(HTTP_BAD_REQUEST);
+ if (tptr == urlcopy + 1) {
+@@ -2408,7 +2336,7 @@ int httpd_main(int argc UNUSED_PARAM, ch
+ , &verbose
+ );
+ if (opt & OPT_DECODE_URL) {
+- fputs(decodeString(url_for_decode, 1), stdout);
++ fputs(percent_decode_in_place(url_for_decode, /*strict:*/ 0), stdout);
+ return 0;
+ }
+ #if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
+--- a/networking/wget.c
++++ b/networking/wget.c
+@@ -298,8 +298,13 @@ static void parse_url(const char *src_ur
+
+ sp = strrchr(h->host, '@');
+ if (sp != NULL) {
+- h->user = h->host;
++ // URL-decode "user:password" string before base64-encoding:
++ // wget http://test:my%20pass@example.com should send
++ // Authorization: Basic dGVzdDpteSBwYXNz
++ // which decodes to "test:my pass".
++ // Standard wget and curl do this too.
+ *sp = '\0';
++ h->user = percent_decode_in_place(h->host, /*strict:*/ 0);
+ h->host = sp + 1;
+ }
+
+@@ -661,12 +666,6 @@ static void download_one_url(const char
+
+ #if ENABLE_FEATURE_WGET_AUTHENTICATION
+ if (target.user) {
+-//TODO: URL-decode "user:password" string before base64-encoding:
+-//wget http://test:my%20pass@example.com should send
+-// Authorization: Basic dGVzdDpteSBwYXNz
+-//which decodes to "test:my pass", instead of what we send now:
+-// Authorization: Basic dGVzdDpteSUyMHBhc3M=
+-//Can reuse decodeString() from httpd.c
+ fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
+ base64enc(target.user));
+ }
diff --git a/package/busybox/patches/005-resource_h_include.patch b/package/busybox/patches/005-resource_h_include.patch
new file mode 100644
index 000000000..d66d66d67
--- /dev/null
+++ b/package/busybox/patches/005-resource_h_include.patch
@@ -0,0 +1,10 @@
+--- a/include/libbb.h
++++ b/include/libbb.h
+@@ -35,6 +35,7 @@
+ #include <sys/poll.h>
+ #include <sys/ioctl.h>
+ #include <sys/mman.h>
++#include <sys/resource.h>
+ #include <sys/socket.h>
+ #include <sys/stat.h>
+ #include <sys/time.h>
diff --git a/package/busybox/patches/006-upstream_CVE-2011-2716_fixes.patch b/package/busybox/patches/006-upstream_CVE-2011-2716_fixes.patch
new file mode 100644
index 000000000..94d5f34a1
--- /dev/null
+++ b/package/busybox/patches/006-upstream_CVE-2011-2716_fixes.patch
@@ -0,0 +1,164 @@
+--- busybox-1.19.4/networking/udhcp/common.c
++++ busybox-1.19.4-udhcp/networking/udhcp/common.c
+@@ -29,16 +29,16 @@ const struct dhcp_optflag dhcp_optflags[
+ // { OPTION_IP | OPTION_LIST , 0x07 }, /* DHCP_LOG_SERVER */
+ // { OPTION_IP | OPTION_LIST , 0x08 }, /* DHCP_COOKIE_SERVER */
+ { OPTION_IP | OPTION_LIST , 0x09 }, /* DHCP_LPR_SERVER */
+- { OPTION_STRING | OPTION_REQ, 0x0c }, /* DHCP_HOST_NAME */
++ { OPTION_STRING_HOST | OPTION_REQ, 0x0c }, /* DHCP_HOST_NAME */
+ { OPTION_U16 , 0x0d }, /* DHCP_BOOT_SIZE */
+- { OPTION_STRING | OPTION_REQ, 0x0f }, /* DHCP_DOMAIN_NAME */
++ { OPTION_STRING_HOST | OPTION_REQ, 0x0f }, /* DHCP_DOMAIN_NAME */
+ { OPTION_IP , 0x10 }, /* DHCP_SWAP_SERVER */
+ { OPTION_STRING , 0x11 }, /* DHCP_ROOT_PATH */
+ { OPTION_U8 , 0x17 }, /* DHCP_IP_TTL */
+ { OPTION_U16 , 0x1a }, /* DHCP_MTU */
+ { OPTION_IP | OPTION_REQ, 0x1c }, /* DHCP_BROADCAST */
+ { OPTION_IP_PAIR | OPTION_LIST , 0x21 }, /* DHCP_ROUTES */
+- { OPTION_STRING , 0x28 }, /* DHCP_NIS_DOMAIN */
++ { OPTION_STRING_HOST , 0x28 }, /* DHCP_NIS_DOMAIN */
+ { OPTION_IP | OPTION_LIST , 0x29 }, /* DHCP_NIS_SERVER */
+ { OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a }, /* DHCP_NTP_SERVER */
+ { OPTION_IP | OPTION_LIST , 0x2c }, /* DHCP_WINS_SERVER */
+@@ -46,7 +46,7 @@ const struct dhcp_optflag dhcp_optflags[
+ { OPTION_IP , 0x36 }, /* DHCP_SERVER_ID */
+ { OPTION_STRING , 0x38 }, /* DHCP_ERR_MESSAGE */
+ //TODO: must be combined with 'sname' and 'file' handling:
+- { OPTION_STRING , 0x42 }, /* DHCP_TFTP_SERVER_NAME */
++ { OPTION_STRING_HOST , 0x42 }, /* DHCP_TFTP_SERVER_NAME */
+ { OPTION_STRING , 0x43 }, /* DHCP_BOOT_FILE */
+ //TODO: not a string, but a set of LASCII strings:
+ // { OPTION_STRING , 0x4D }, /* DHCP_USER_CLASS */
+@@ -143,6 +143,7 @@ const uint8_t dhcp_option_lengths[] ALIG
+ [OPTION_IP_PAIR] = 8,
+ // [OPTION_BOOLEAN] = 1,
+ [OPTION_STRING] = 1, /* ignored by udhcp_str2optset */
++ [OPTION_STRING_HOST] = 1, /* ignored by udhcp_str2optset */
+ #if ENABLE_FEATURE_UDHCP_RFC3397
+ [OPTION_DNS_STRING] = 1, /* ignored by both udhcp_str2optset and xmalloc_optname_optval */
+ [OPTION_SIP_SERVERS] = 1,
+@@ -411,7 +412,9 @@ static NOINLINE void attach_option(
+ /* actually 255 is ok too, but adding a space can overlow it */
+
+ existing->data = xrealloc(existing->data, OPT_DATA + 1 + old_len + length);
+- if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING) {
++ if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING
++ || (optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING_HOST
++ ) {
+ /* add space separator between STRING options in a list */
+ existing->data[OPT_DATA + old_len] = ' ';
+ old_len++;
+@@ -475,6 +478,7 @@ int FAST_FUNC udhcp_str2optset(const cha
+ retval = udhcp_str2nip(val, buffer + 4);
+ break;
+ case OPTION_STRING:
++ case OPTION_STRING_HOST:
+ #if ENABLE_FEATURE_UDHCP_RFC3397
+ case OPTION_DNS_STRING:
+ #endif
+--- busybox-1.19.4/networking/udhcp/common.h
++++ busybox-1.19.4-udhcp/networking/udhcp/common.h
+@@ -80,6 +80,9 @@ enum {
+ OPTION_IP = 1,
+ OPTION_IP_PAIR,
+ OPTION_STRING,
++ /* Opts of STRING_HOST type will be sanitized before they are passed
++ * to udhcpc script's environment: */
++ OPTION_STRING_HOST,
+ // OPTION_BOOLEAN,
+ OPTION_U8,
+ OPTION_U16,
+--- busybox-1.19.4/networking/udhcp/dhcpc.c
++++ busybox-1.19.4-udhcp/networking/udhcp/dhcpc.c
+@@ -101,6 +101,7 @@ static const uint8_t len_of_option_as_st
+ [OPTION_IP_PAIR ] = sizeof("255.255.255.255 ") * 2,
+ [OPTION_STATIC_ROUTES ] = sizeof("255.255.255.255/32 255.255.255.255 "),
+ [OPTION_STRING ] = 1,
++ [OPTION_STRING_HOST ] = 1,
+ #if ENABLE_FEATURE_UDHCP_RFC3397
+ [OPTION_DNS_STRING ] = 1, /* unused */
+ /* Hmmm, this severely overestimates size if SIP_SERVERS option
+@@ -135,6 +136,63 @@ static int mton(uint32_t mask)
+ return i;
+ }
+
++/* Check if a given label represents a valid DNS label
++ * Return pointer to the first character after the label upon success,
++ * NULL otherwise.
++ * See RFC1035, 2.3.1
++ */
++/* We don't need to be particularly anal. For example, allowing _, hyphen
++ * at the end, or leading and trailing dots would be ok, since it
++ * can't be used for attacks. (Leading hyphen can be, if someone uses
++ * cmd "$hostname"
++ * in the script: then hostname may be treated as an option)
++ */
++static const char *valid_domain_label(const char *label)
++{
++ unsigned char ch;
++ unsigned pos = 0;
++
++ for (;;) {
++ ch = *label;
++ if ((ch|0x20) < 'a' || (ch|0x20) > 'z') {
++ if (pos == 0) {
++ /* label must begin with letter */
++ return NULL;
++ }
++ if (ch < '0' || ch > '9') {
++ if (ch == '\0' || ch == '.')
++ return label;
++ /* DNS allows only '-', but we are more permissive */
++ if (ch != '-' && ch != '_')
++ return NULL;
++ }
++ }
++ label++;
++ pos++;
++ //Do we want this?
++ //if (pos > 63) /* NS_MAXLABEL; labels must be 63 chars or less */
++ // return NULL;
++ }
++}
++
++/* Check if a given name represents a valid DNS name */
++/* See RFC1035, 2.3.1 */
++static int good_hostname(const char *name)
++{
++ //const char *start = name;
++
++ for (;;) {
++ name = valid_domain_label(name);
++ if (!name)
++ return 0;
++ if (!name[0])
++ return 1;
++ //Do we want this?
++ //return ((name - start) < 1025); /* NS_MAXDNAME */
++ name++;
++ }
++}
++
+ /* Create "opt_name=opt_value" string */
+ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_optflag *optflag, const char *opt_name)
+ {
+@@ -185,8 +243,11 @@ static NOINLINE char *xmalloc_optname_op
+ break;
+ }
+ case OPTION_STRING:
++ case OPTION_STRING_HOST:
+ memcpy(dest, option, len);
+ dest[len] = '\0';
++ if (type == OPTION_STRING_HOST && !good_hostname(dest))
++ safe_strncpy(dest, "bad", len);
+ return ret; /* Short circuit this case */
+ case OPTION_STATIC_ROUTES: {
+ /* Option binary format:
+@@ -314,6 +375,7 @@ static char **fill_envp(struct dhcp_pack
+ /* +1 element for each option, +2 for subnet option: */
+ if (packet) {
+ /* note: do not search for "pad" (0) and "end" (255) options */
++//TODO: change logic to scan packet _once_
+ for (i = 1; i < 255; i++) {
+ temp = udhcp_get_option(packet, i);
+ if (temp) {
diff --git a/package/busybox/patches/110-wget_getopt_fix.patch b/package/busybox/patches/110-wget_getopt_fix.patch
new file mode 100644
index 000000000..100323146
--- /dev/null
+++ b/package/busybox/patches/110-wget_getopt_fix.patch
@@ -0,0 +1,11 @@
+--- a/networking/wget.c
++++ b/networking/wget.c
+@@ -873,7 +873,7 @@ int wget_main(int argc UNUSED_PARAM, cha
+ /* Ignored: */
+ // "tries\0" Required_argument "t"
+ /* Ignored (we always use PASV): */
+- "passive-ftp\0" No_argument "\xff"
++ "passive-ftp\0" No_argument "\xfd"
+ "header\0" Required_argument "\xfe"
+ "post-data\0" Required_argument "\xfd"
+ /* Ignored (we don't do ssl) */
diff --git a/package/busybox/patches/140-trylink_bash.patch b/package/busybox/patches/140-trylink_bash.patch
new file mode 100644
index 000000000..f9571fcbc
--- /dev/null
+++ b/package/busybox/patches/140-trylink_bash.patch
@@ -0,0 +1,8 @@
+--- a/scripts/trylink
++++ b/scripts/trylink
+@@ -1,4 +1,4 @@
+-#!/bin/sh
++#!/usr/bin/env bash
+
+ debug=false
+
diff --git a/package/busybox/patches/141-gen_build_files_bash.patch b/package/busybox/patches/141-gen_build_files_bash.patch
new file mode 100644
index 000000000..d258fb8bc
--- /dev/null
+++ b/package/busybox/patches/141-gen_build_files_bash.patch
@@ -0,0 +1,8 @@
+--- a/scripts/gen_build_files.sh
++++ b/scripts/gen_build_files.sh
+@@ -1,4 +1,4 @@
+-#!/bin/sh
++#!/usr/bin/env bash
+
+ # Note: was using sed OPTS CMD -- FILES
+ # but users complain that many sed implementations
diff --git a/package/busybox/patches/150-no_static_libgcc.patch b/package/busybox/patches/150-no_static_libgcc.patch
new file mode 100644
index 000000000..fde6fbb86
--- /dev/null
+++ b/package/busybox/patches/150-no_static_libgcc.patch
@@ -0,0 +1,11 @@
+--- a/Makefile.flags
++++ b/Makefile.flags
+@@ -50,7 +50,7 @@ CFLAGS += $(call cc-option,-fno-builtin-
+ # -fno-guess-branch-probability: prohibit pseudo-random guessing
+ # of branch probabilities (hopefully makes bloatcheck more stable):
+ CFLAGS += $(call cc-option,-fno-guess-branch-probability,)
+-CFLAGS += $(call cc-option,-funsigned-char -static-libgcc,)
++CFLAGS += $(call cc-option,-funsigned-char,)
+ CFLAGS += $(call cc-option,-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1,)
+
+ # FIXME: These warnings are at least partially to be concerned about and should
diff --git a/package/busybox/patches/200-etc_crontabs.patch b/package/busybox/patches/200-etc_crontabs.patch
new file mode 100644
index 000000000..ebf299050
--- /dev/null
+++ b/package/busybox/patches/200-etc_crontabs.patch
@@ -0,0 +1,22 @@
+--- a/miscutils/crond.c
++++ b/miscutils/crond.c
+@@ -37,7 +37,7 @@
+
+
+ #define TMPDIR CONFIG_FEATURE_CROND_DIR
+-#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs"
++#define CRONTABS "/etc/crontabs"
+ #ifndef SENDMAIL
+ # define SENDMAIL "sendmail"
+ #endif
+--- a/miscutils/crontab.c
++++ b/miscutils/crontab.c
+@@ -22,7 +22,7 @@
+
+ #include "libbb.h"
+
+-#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs"
++#define CRONTABS "/etc/crontabs"
+ #ifndef CRONUPDATE
+ #define CRONUPDATE "cron.update"
+ #endif
diff --git a/package/busybox/patches/240-udhcpc_retries.patch b/package/busybox/patches/240-udhcpc_retries.patch
new file mode 100644
index 000000000..a9f4f3d1e
--- /dev/null
+++ b/package/busybox/patches/240-udhcpc_retries.patch
@@ -0,0 +1,20 @@
+--- a/networking/udhcp/dhcpc.c
++++ b/networking/udhcp/dhcpc.c
+@@ -1257,7 +1257,7 @@ int udhcpc_main(int argc UNUSED_PARAM, c
+
+ switch (state) {
+ case INIT_SELECTING:
+- if (packet_num < discover_retries) {
++ if (!discover_retries || packet_num < discover_retries) {
+ if (packet_num == 0)
+ xid = random_xid();
+ /* broadcast */
+@@ -1286,7 +1286,7 @@ int udhcpc_main(int argc UNUSED_PARAM, c
+ packet_num = 0;
+ continue;
+ case REQUESTING:
+- if (packet_num < discover_retries) {
++ if (!discover_retries || packet_num < discover_retries) {
+ /* send broadcast select packet */
+ send_select(xid, server_addr, requested_ip);
+ timeout = discover_timeout;
diff --git a/package/busybox/patches/241-udhcpc-oversized_packets.patch b/package/busybox/patches/241-udhcpc-oversized_packets.patch
new file mode 100644
index 000000000..471d704a7
--- /dev/null
+++ b/package/busybox/patches/241-udhcpc-oversized_packets.patch
@@ -0,0 +1,99 @@
+--- a/networking/udhcp/packet.c
++++ b/networking/udhcp/packet.c
+@@ -158,6 +158,11 @@ uint16_t FAST_FUNC udhcp_checksum(void *
+ return ~sum;
+ }
+
++int udhcp_get_payload_len(struct dhcp_packet *dhcp_pkt)
++{
++ return sizeof(struct dhcp_packet) - DHCP_OPTIONS_BUFSIZE + udhcp_end_option(dhcp_pkt->options) + sizeof(dhcp_pkt->options[0]);
++}
++
+ /* Construct a ip/udp header for a packet, send packet */
+ int FAST_FUNC udhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
+ uint32_t source_nip, int source_port,
+@@ -166,10 +171,10 @@ int FAST_FUNC udhcp_send_raw_packet(stru
+ {
+ struct sockaddr_ll dest_sll;
+ struct ip_udp_dhcp_packet packet;
+- unsigned padding;
+ int fd;
+ int result = -1;
+ const char *msg;
++ int p_len = udhcp_get_payload_len(dhcp_pkt);
+
+ fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+ if (fd < 0) {
+@@ -178,8 +183,8 @@ int FAST_FUNC udhcp_send_raw_packet(stru
+ }
+
+ memset(&dest_sll, 0, sizeof(dest_sll));
+- memset(&packet, 0, offsetof(struct ip_udp_dhcp_packet, data));
+- packet.data = *dhcp_pkt; /* struct copy */
++ memset(&packet, 0, sizeof(packet));
++ memcpy(&(packet.data), dhcp_pkt, p_len);
+
+ dest_sll.sll_family = AF_PACKET;
+ dest_sll.sll_protocol = htons(ETH_P_IP);
+@@ -192,36 +197,24 @@ int FAST_FUNC udhcp_send_raw_packet(stru
+ goto ret_close;
+ }
+
+- /* We were sending full-sized DHCP packets (zero padded),
+- * but some badly configured servers were seen dropping them.
+- * Apparently they drop all DHCP packets >576 *ethernet* octets big,
+- * whereas they may only drop packets >576 *IP* octets big
+- * (which for typical Ethernet II means 590 octets: 6+6+2 + 576).
+- *
+- * In order to work with those buggy servers,
+- * we truncate packets after end option byte.
+- */
+- padding = DHCP_OPTIONS_BUFSIZE - 1 - udhcp_end_option(packet.data.options);
+-
+ packet.ip.protocol = IPPROTO_UDP;
+ packet.ip.saddr = source_nip;
+ packet.ip.daddr = dest_nip;
+ packet.udp.source = htons(source_port);
+ packet.udp.dest = htons(dest_port);
+- /* size, excluding IP header: */
+- packet.udp.len = htons(UDP_DHCP_SIZE - padding);
+- /* for UDP checksumming, ip.len is set to UDP packet len */
++ p_len += sizeof(packet.udp);
++ packet.udp.len = htons(p_len);
+ packet.ip.tot_len = packet.udp.len;
+- packet.udp.check = udhcp_checksum(&packet, IP_UDP_DHCP_SIZE - padding);
+- /* but for sending, it is set to IP packet len */
+- packet.ip.tot_len = htons(IP_UDP_DHCP_SIZE - padding);
++ p_len += sizeof(packet.ip);
++ packet.udp.check = udhcp_checksum(&packet, p_len);
++ packet.ip.tot_len = htons(p_len);
+ packet.ip.ihl = sizeof(packet.ip) >> 2;
+ packet.ip.version = IPVERSION;
+ packet.ip.ttl = IPDEFTTL;
+ packet.ip.check = udhcp_checksum(&packet.ip, sizeof(packet.ip));
+
+ udhcp_dump_packet(dhcp_pkt);
+- result = sendto(fd, &packet, IP_UDP_DHCP_SIZE - padding, /*flags:*/ 0,
++ result = sendto(fd, &packet, p_len, /*flags:*/ 0,
+ (struct sockaddr *) &dest_sll, sizeof(dest_sll));
+ msg = "sendto";
+ ret_close:
+@@ -239,7 +232,6 @@ int FAST_FUNC udhcp_send_kernel_packet(s
+ uint32_t dest_nip, int dest_port)
+ {
+ struct sockaddr_in client;
+- unsigned padding;
+ int fd;
+ int result = -1;
+ const char *msg;
+@@ -270,9 +262,7 @@ int FAST_FUNC udhcp_send_kernel_packet(s
+ }
+
+ udhcp_dump_packet(dhcp_pkt);
+-
+- padding = DHCP_OPTIONS_BUFSIZE - 1 - udhcp_end_option(dhcp_pkt->options);
+- result = safe_write(fd, dhcp_pkt, DHCP_SIZE - padding);
++ result = safe_write(fd, dhcp_pkt, udhcp_get_payload_len(dhcp_pkt));
+ msg = "write";
+ ret_close:
+ close(fd);
diff --git a/package/busybox/patches/242-udhcpc_msgs.patch b/package/busybox/patches/242-udhcpc_msgs.patch
new file mode 100644
index 000000000..998e3b664
--- /dev/null
+++ b/package/busybox/patches/242-udhcpc_msgs.patch
@@ -0,0 +1,18 @@
+--- a/networking/udhcp/dhcpc.c
++++ b/networking/udhcp/dhcpc.c
+@@ -540,6 +540,7 @@ static int raw_bcast_from_client_config_
+ static NOINLINE int send_discover(uint32_t xid, uint32_t requested)
+ {
+ struct dhcp_packet packet;
++ static int msgs = 0;
+
+ /* Fill in: op, htype, hlen, cookie, chaddr fields,
+ * random xid field (we override it below),
+@@ -557,6 +558,7 @@ static NOINLINE int send_discover(uint32
+ */
+ add_client_options(&packet);
+
++ if (msgs++ < 3)
+ bb_info_msg("Sending discover...");
+ return raw_bcast_from_client_config_ifindex(&packet);
+ }
diff --git a/package/busybox/patches/243-udhcpc_changed_ifindex.patch b/package/busybox/patches/243-udhcpc_changed_ifindex.patch
new file mode 100644
index 000000000..4b28166bc
--- /dev/null
+++ b/package/busybox/patches/243-udhcpc_changed_ifindex.patch
@@ -0,0 +1,15 @@
+--- a/networking/udhcp/dhcpc.c
++++ b/networking/udhcp/dhcpc.c
+@@ -1205,6 +1205,12 @@ int udhcpc_main(int argc UNUSED_PARAM, c
+ /* silence "uninitialized!" warning */
+ unsigned timestamp_before_wait = timestamp_before_wait;
+
++ /* When running on a bridge, the ifindex may have changed (e.g. if
++ * member interfaces were added/removed or if the status of the
++ * bridge changed).
++ * Workaround: refresh it here before processing the next packet */
++ udhcp_read_interface(client_config.interface, &client_config.ifindex, NULL, client_config.client_mac);
++
+ //bb_error_msg("sockfd:%d, listen_mode:%d", sockfd, listen_mode);
+
+ /* Was opening raw or udp socket here
diff --git a/package/busybox/patches/244-udhcpc_add_6rd_option.patch b/package/busybox/patches/244-udhcpc_add_6rd_option.patch
new file mode 100644
index 000000000..9e30ef79a
--- /dev/null
+++ b/package/busybox/patches/244-udhcpc_add_6rd_option.patch
@@ -0,0 +1,143 @@
+--- a/networking/udhcp/common.c
++++ b/networking/udhcp/common.c
+@@ -60,6 +60,8 @@ const struct dhcp_optflag dhcp_optflags[
+ { OPTION_U8 , 0x85 }, /* DHCP_VLAN_PRIORITY */
+ #endif
+ { OPTION_STATIC_ROUTES , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */
++ { OPTION_6RD , 0xd4 }, /* DHCP_6RD (RFC) */
++ { OPTION_6RD , 0x96 }, /* DHCP_6RD (Comcast) */
+ { OPTION_STRING , 0xfc }, /* DHCP_WPAD */
+
+ /* Options below have no match in dhcp_option_strings[],
+@@ -127,6 +129,8 @@ const char dhcp_option_strings[] ALIGN1
+ "vlanpriority" "\0"/* DHCP_VLAN_PRIORITY */
+ #endif
+ "msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */
++ "ip6rd" "\0" /* DHCP_6RD (RFC) */
++ "ip6rd" "\0" /* DHCP_6RD (Comcast) */
+ "wpad" "\0" /* DHCP_WPAD */
+ ;
+
+@@ -154,6 +158,7 @@ const uint8_t dhcp_option_lengths[] ALIG
+ [OPTION_S32] = 4,
+ /* Just like OPTION_STRING, we use minimum length here */
+ [OPTION_STATIC_ROUTES] = 5,
++ [OPTION_6RD] = 22,
+ };
+
+
+--- a/networking/udhcp/common.h
++++ b/networking/udhcp/common.h
+@@ -88,6 +88,7 @@ enum {
+ OPTION_S32,
+ OPTION_BIN,
+ OPTION_STATIC_ROUTES,
++ OPTION_6RD,
+ #if ENABLE_FEATURE_UDHCP_RFC3397
+ OPTION_DNS_STRING, /* RFC1035 compressed domain name list */
+ OPTION_SIP_SERVERS,
+--- a/networking/udhcp/dhcpc.c
++++ b/networking/udhcp/dhcpc.c
+@@ -100,6 +100,7 @@ static const uint8_t len_of_option_as_st
+ [OPTION_IP ] = sizeof("255.255.255.255 "),
+ [OPTION_IP_PAIR ] = sizeof("255.255.255.255 ") * 2,
+ [OPTION_STATIC_ROUTES ] = sizeof("255.255.255.255/32 255.255.255.255 "),
++ [OPTION_6RD ] = sizeof("32 128 FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF 255.255.255.255 "),
+ [OPTION_STRING ] = 1,
+ #if ENABLE_FEATURE_UDHCP_RFC3397
+ [OPTION_DNS_STRING ] = 1, /* unused */
+@@ -123,6 +124,23 @@ static int sprint_nip(char *dest, const
+ return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
+ }
+
++static int sprint_nip6(char *dest, const char *pre, const uint8_t *ip)
++{
++ int len = 0;
++ int off;
++ uint16_t word;
++
++ len += sprintf(dest, "%s", pre);
++
++ for (off = 0; off < 16; off += 2)
++ {
++ move_from_unaligned16(word, &ip[off]);
++ len += sprintf(dest+len, "%s%04X", off ? ":" : "", htons(word));
++ }
++
++ return len;
++}
++
+ /* really simple implementation, just count the bits */
+ static int mton(uint32_t mask)
+ {
+@@ -231,6 +249,70 @@ static NOINLINE char *xmalloc_optname_op
+ }
+
+ return ret;
++ }
++ case OPTION_6RD: {
++ /* Option binary format:
++ * 0 1 2 3
++ * 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
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * | OPTION_6RD | option-length | IPv4MaskLen | 6rdPrefixLen |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * | |
++ * | 6rdPrefix |
++ * | (16 octets) |
++ * | |
++ * | |
++ * | |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * | 6rdBRIPv4Address(es) |
++ * . .
++ * . .
++ * . .
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ *
++ * We convert it to a string "IPv4MaskLen 6rdPrefixLen 6rdPrefix 6rdBRIPv4Address"
++ */
++
++ /* Sanity check: ensure that our length is at least 22 bytes, that
++ * IPv4MaskLen is <= 32, 6rdPrefixLen <= 128 and that the sum of
++ * (32 - IPv4MaskLen) + 6rdPrefixLen is less than or equal to 128.
++ * If any of these requirements is not fulfilled, return with empty
++ * value.
++ */
++ if ((len >= 22) && (*option <= 32) && (*(option+1) <= 128) &&
++ (((32 - *option) + *(option+1)) <= 128))
++ {
++ /* IPv4MaskLen */
++ dest += sprintf(dest, "%u ", *option++);
++ len--;
++
++ /* 6rdPrefixLen */
++ dest += sprintf(dest, "%u ", *option++);
++ len--;
++
++ /* 6rdPrefix */
++ dest += sprint_nip6(dest, "", option);
++ option += 16;
++ len -= 16;
++
++ /* 6rdBRIPv4Addresses */
++ while (len >= 4)
++ {
++ dest += sprint_nip(dest, " ", option);
++ option += 4;
++ len -= 4;
++
++ /* the code to determine the option size fails to work with
++ * lengths that are not a multiple of the minimum length,
++ * adding all advertised 6rdBRIPv4Addresses here would
++ * overflow the destination buffer, therefore skip the rest
++ * for now
++ */
++ break;
++ }
++ }
++
++ return ret;
+ }
+ #if ENABLE_FEATURE_UDHCP_RFC3397
+ case OPTION_DNS_STRING:
diff --git a/package/busybox/patches/250-ash_export-n.patch b/package/busybox/patches/250-ash_export-n.patch
new file mode 100644
index 000000000..048c236b8
--- /dev/null
+++ b/package/busybox/patches/250-ash_export-n.patch
@@ -0,0 +1,34 @@
+--- a/shell/ash.c
++++ b/shell/ash.c
+@@ -12611,8 +12611,17 @@ exportcmd(int argc UNUSED_PARAM, char **
+ const char *p;
+ char **aptr;
+ int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
++ int mask = ~0;
++ int nopt;
++ while ((nopt = nextopt("np"))) {
++ if (nopt == 'n') {
++ mask = ~flag;
++ } else { /* p */
++ break;
++ }
++ }
+
+- if (nextopt("p") != 'p') {
++ if (nopt != 'p') {
+ aptr = argptr;
+ name = *aptr;
+ if (name) {
+@@ -12624,10 +12633,12 @@ exportcmd(int argc UNUSED_PARAM, char **
+ vp = *findvar(hashvar(name), name);
+ if (vp) {
+ vp->flags |= flag;
++ vp->flags &= mask;
+ continue;
+ }
+ }
+ setvar(name, p, flag);
++ setvar(name, p, flag & mask);
+ } while ((name = *++aptr) != NULL);
+ return 0;
+ }
diff --git a/package/busybox/patches/251-ash_fix-redir-substitution.patch b/package/busybox/patches/251-ash_fix-redir-substitution.patch
new file mode 100644
index 000000000..cb2ae94e0
--- /dev/null
+++ b/package/busybox/patches/251-ash_fix-redir-substitution.patch
@@ -0,0 +1,12 @@
+--- a/shell/ash.c
++++ b/shell/ash.c
+@@ -6846,8 +6846,7 @@ evalvar(char *p, int flags, struct strli
+ patloc = expdest - (char *)stackblock();
+ if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
+ startloc, varflags,
+-//TODO: | EXP_REDIR too? All other such places do it too
+- /* quotes: */ flags & (EXP_FULL | EXP_CASE),
++ /* quotes: */ flags & (EXP_FULL | EXP_CASE | EXP_REDIR),
+ var_str_list)
+ ) {
+ int amount = expdest - (
diff --git a/package/busybox/patches/300-netmsg.patch b/package/busybox/patches/300-netmsg.patch
new file mode 100644
index 000000000..e8b2513eb
--- /dev/null
+++ b/package/busybox/patches/300-netmsg.patch
@@ -0,0 +1,103 @@
+--- a/include/applets.src.h
++++ b/include/applets.src.h
+@@ -266,6 +266,7 @@ IF_MT(APPLET(mt, BB_DIR_BIN, BB_SUID_DRO
+ IF_MV(APPLET(mv, BB_DIR_BIN, BB_SUID_DROP))
+ IF_NAMEIF(APPLET(nameif, BB_DIR_SBIN, BB_SUID_DROP))
+ IF_NC(APPLET(nc, BB_DIR_USR_BIN, BB_SUID_DROP))
++IF_NETMSG(APPLET(netmsg, BB_DIR_BIN, BB_SUID_REQUIRE))
+ IF_NETSTAT(APPLET(netstat, BB_DIR_BIN, BB_SUID_DROP))
+ IF_NICE(APPLET(nice, BB_DIR_BIN, BB_SUID_DROP))
+ IF_NOHUP(APPLET(nohup, BB_DIR_USR_BIN, BB_SUID_DROP))
+--- a/networking/Config.src
++++ b/networking/Config.src
+@@ -612,6 +612,12 @@ config FEATURE_IPCALC_LONG_OPTIONS
+ help
+ Support long options for the ipcalc applet.
+
++config NETMSG
++ bool "netmsg"
++ default n
++ help
++ simple program for sending udp broadcast messages
++
+ config NETSTAT
+ bool "netstat"
+ default y
+--- a/networking/Kbuild.src
++++ b/networking/Kbuild.src
+@@ -27,6 +27,7 @@ lib-$(CONFIG_IP) += ip.o
+ lib-$(CONFIG_IPCALC) += ipcalc.o
+ lib-$(CONFIG_NAMEIF) += nameif.o
+ lib-$(CONFIG_NC) += nc.o
++lib-$(CONFIG_NETMSG) += netmsg.o
+ lib-$(CONFIG_NETSTAT) += netstat.o
+ lib-$(CONFIG_NSLOOKUP) += nslookup.o
+ lib-$(CONFIG_NTPD) += ntpd.o
+--- /dev/null
++++ b/networking/netmsg.c
+@@ -0,0 +1,65 @@
++/*
++ * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
++ *
++ * This is free software, licensed under the GNU General Public License v2.
++ */
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <netdb.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include "busybox.h"
++
++//usage:#define netmsg_trivial_usage NOUSAGE_STR
++//usage:#define netmsg_full_usage ""
++
++#ifndef CONFIG_NETMSG
++int main(int argc, char **argv)
++#else
++int netmsg_main(int argc, char **argv)
++#endif
++{
++ int s;
++ struct sockaddr_in addr;
++ int optval = 1;
++ unsigned char buf[1001];
++
++ if (argc != 3) {
++ fprintf(stderr, "usage: %s <ip> \"<message>\"\n", argv[0]);
++ exit(1);
++ }
++
++ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
++ perror("Opening socket");
++ exit(1);
++ }
++
++ memset(&addr, 0, sizeof(addr));
++ addr.sin_family = AF_INET;
++ addr.sin_addr.s_addr = inet_addr(argv[1]);
++ addr.sin_port = htons(0x1337);
++
++ memset(buf, 0, 1001);
++ buf[0] = 0xde;
++ buf[1] = 0xad;
++
++ strncpy(buf + 2, argv[2], 998);
++
++ if (setsockopt (s, SOL_SOCKET, SO_BROADCAST, (caddr_t) &optval, sizeof (optval)) < 0) {
++ perror("setsockopt()");
++ goto fail;
++ }
++
++ if (sendto(s, buf, 1001, 0, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
++ perror("sendto()");
++ goto fail;
++ }
++
++ return 0;
++
++fail:
++ close(s);
++ exit(1);
++}
diff --git a/package/busybox/patches/310-passwd_access.patch b/package/busybox/patches/310-passwd_access.patch
new file mode 100644
index 000000000..daa1b9998
--- /dev/null
+++ b/package/busybox/patches/310-passwd_access.patch
@@ -0,0 +1,41 @@
+
+ Copyright (C) 2006 OpenWrt.org
+
+--- a/networking/httpd.c
++++ b/networking/httpd.c
+@@ -1700,21 +1700,32 @@ static int check_user_passwd(const char
+
+ if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
+ char *md5_passwd;
++ int user_len_p1;
+
+ md5_passwd = strchr(cur->after_colon, ':');
+- if (md5_passwd && md5_passwd[1] == '$' && md5_passwd[2] == '1'
++ user_len_p1 = md5_passwd + 1 - cur->after_colon;
++ if (md5_passwd && !strncmp(md5_passwd + 1, "$p$", 3)) {
++ struct passwd *pwd = NULL;
++
++ pwd = getpwnam(&md5_passwd[4]);
++ if(!pwd->pw_passwd || !pwd->pw_passwd[0] || pwd->pw_passwd[0] == '!')
++ return 1;
++
++ md5_passwd = pwd->pw_passwd;
++ goto check_md5_pw;
++ } else if (md5_passwd && md5_passwd[1] == '$' && md5_passwd[2] == '1'
+ && md5_passwd[3] == '$' && md5_passwd[4]
+ ) {
+ char *encrypted;
+- int r, user_len_p1;
++ int r;
+
+ md5_passwd++;
+- user_len_p1 = md5_passwd - cur->after_colon;
+ /* comparing "user:" */
+ if (strncmp(cur->after_colon, user_and_passwd, user_len_p1) != 0) {
+ continue;
+ }
+
++check_md5_pw:
+ encrypted = pw_encrypt(
+ user_and_passwd + user_len_p1 /* cleartext pwd from user */,
+ md5_passwd /*salt */, 1 /* cleanup */);
diff --git a/package/busybox/patches/340-lock_util.patch b/package/busybox/patches/340-lock_util.patch
new file mode 100644
index 000000000..eb346421a
--- /dev/null
+++ b/package/busybox/patches/340-lock_util.patch
@@ -0,0 +1,172 @@
+--- a/include/applets.src.h
++++ b/include/applets.src.h
+@@ -218,6 +218,7 @@ IF_LN(APPLET_NOEXEC(ln, ln, BB_DIR_BIN,
+ IF_LOAD_POLICY(APPLET(load_policy, BB_DIR_USR_SBIN, BB_SUID_DROP))
+ IF_LOADFONT(APPLET(loadfont, BB_DIR_USR_SBIN, BB_SUID_DROP))
+ IF_LOADKMAP(APPLET(loadkmap, BB_DIR_SBIN, BB_SUID_DROP))
++IF_LOCK(APPLET(lock, BB_DIR_BIN, BB_SUID_DROP))
+ IF_LOGGER(APPLET(logger, BB_DIR_USR_BIN, BB_SUID_DROP))
+ /* Needs to be run by root or be suid root - needs to change uid and gid: */
+ IF_LOGIN(APPLET(login, BB_DIR_BIN, BB_SUID_REQUIRE))
+--- a/miscutils/Config.src
++++ b/miscutils/Config.src
+@@ -419,6 +419,11 @@ config FEATURE_HDPARM_HDIO_GETSET_DMA
+ help
+ Enables the 'hdparm -d' option to get/set using_dma flag.
+
++config LOCK
++ bool "lock"
++ help
++ Small utility for using locks in scripts
++
+ config MAKEDEVS
+ bool "makedevs"
+ default y
+--- a/miscutils/Kbuild.src
++++ b/miscutils/Kbuild.src
+@@ -29,6 +29,7 @@ lib-$(CONFIG_INOTIFYD) += inotifyd.o
+ lib-$(CONFIG_FEATURE_LAST_SMALL)+= last.o
+ lib-$(CONFIG_FEATURE_LAST_FANCY)+= last_fancy.o
+ lib-$(CONFIG_LESS) += less.o
++lib-$(CONFIG_LOCK) += lock.o
+ lib-$(CONFIG_MAKEDEVS) += makedevs.o
+ lib-$(CONFIG_MAN) += man.o
+ lib-$(CONFIG_MICROCOM) += microcom.o
+--- /dev/null
++++ b/miscutils/lock.c
+@@ -0,0 +1,135 @@
++/*
++ * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
++ *
++ * This is free software, licensed under the GNU General Public License v2.
++ */
++#include <sys/types.h>
++#include <sys/file.h>
++#include <sys/stat.h>
++#include <signal.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <stdio.h>
++#include "busybox.h"
++
++//usage:#define lock_trivial_usage NOUSAGE_STR
++//usage:#define lock_full_usage ""
++
++static int unlock = 0;
++static int shared = 0;
++static int waitonly = 0;
++static int fd;
++static char *file;
++
++static void usage(char *name)
++{
++ fprintf(stderr, "Usage: %s [-suw] <filename>\n"
++ " -s Use shared locking\n"
++ " -u Unlock\n"
++ " -w Wait for the lock to become free, don't acquire lock\n"
++ "\n", name);
++ exit(1);
++}
++
++static void exit_unlock(int sig)
++{
++ flock(fd, LOCK_UN);
++ exit(0);
++}
++
++static int do_unlock(void)
++{
++ FILE *f;
++ int i;
++
++ if ((f = fopen(file, "r")) == NULL)
++ return 0;
++
++ fscanf(f, "%d", &i);
++ if (i > 0)
++ kill(i, SIGTERM);
++
++ fclose(f);
++
++ return 0;
++}
++
++static int do_lock(void)
++{
++ int pid;
++ char pidstr[8];
++
++ if ((fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0700)) < 0) {
++ if ((fd = open(file, O_RDWR)) < 0) {
++ fprintf(stderr, "Can't open %s\n", file);
++ return 1;
++ }
++ }
++
++ if (flock(fd, (shared ? LOCK_SH : LOCK_EX)) < 0) {
++ fprintf(stderr, "Can't lock %s\n", file);
++ return 1;
++ }
++
++ pid = fork();
++
++ if (pid < 0)
++ return -1;
++
++ if (pid == 0) {
++ signal(SIGKILL, exit_unlock);
++ signal(SIGTERM, exit_unlock);
++ signal(SIGINT, exit_unlock);
++ if (waitonly)
++ exit_unlock(0);
++ else
++ while (1)
++ sleep(1);
++ } else {
++ if (!waitonly) {
++ lseek(fd, 0, SEEK_SET);
++ ftruncate(fd, 0);
++ sprintf(pidstr, "%d\n", pid);
++ write(fd, pidstr, strlen(pidstr));
++ close(fd);
++ }
++
++ return 0;
++ }
++ return 0;
++}
++
++int lock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
++int lock_main(int argc, char **argv)
++{
++ char **args = &argv[1];
++ int c = argc - 1;
++
++ while ((*args != NULL) && (*args)[0] == '-') {
++ char *ch = *args;
++ while (*(++ch) > 0) {
++ switch(*ch) {
++ case 'w':
++ waitonly = 1;
++ break;
++ case 's':
++ shared = 1;
++ break;
++ case 'u':
++ unlock = 1;
++ break;
++ }
++ }
++ c--;
++ args++;
++ }
++
++ if (c != 1)
++ usage(argv[0]);
++
++ file = *args;
++ if (unlock)
++ return do_unlock();
++ else
++ return do_lock();
++}
diff --git a/package/busybox/patches/350-httpd_redir.patch b/package/busybox/patches/350-httpd_redir.patch
new file mode 100644
index 000000000..11125e71f
--- /dev/null
+++ b/package/busybox/patches/350-httpd_redir.patch
@@ -0,0 +1,90 @@
+--- a/networking/httpd.c
++++ b/networking/httpd.c
+@@ -105,6 +105,7 @@
+ //usage: IF_FEATURE_HTTPD_BASIC_AUTH(" [-r REALM]")
+ //usage: " [-h HOME]\n"
+ //usage: "or httpd -d/-e" IF_FEATURE_HTTPD_AUTH_MD5("/-m") " STRING"
++//usage: " [-R <path> [-H <host>]]"
+ //usage:#define httpd_full_usage "\n\n"
+ //usage: "Listen for incoming HTTP requests\n"
+ //usage: "\n -i Inetd mode"
+@@ -121,6 +122,8 @@
+ //usage: "\n -m STRING MD5 crypt STRING")
+ //usage: "\n -e STRING HTML encode STRING"
+ //usage: "\n -d STRING URL decode STRING"
++//usage: "\n -R PATH Redirect target path"
++//usage: "\n -H HOST Redirect target host"
+
+ #include "libbb.h"
+ #if ENABLE_FEATURE_HTTPD_USE_SENDFILE
+@@ -272,6 +275,8 @@ struct globals {
+
+ const char *found_mime_type;
+ const char *found_moved_temporarily;
++ const char *redirect_path;
++ const char *redirect_host;
+ Htaccess_IP *ip_a_d; /* config allow/deny lines */
+
+ IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
+@@ -322,6 +327,8 @@ struct globals {
+ #define index_page (G.index_page )
+ #define found_mime_type (G.found_mime_type )
+ #define found_moved_temporarily (G.found_moved_temporarily)
++#define redirect_path (G.redirect_path )
++#define redirect_host (G.redirect_host )
+ #define last_mod (G.last_mod )
+ #define ip_a_d (G.ip_a_d )
+ #define g_realm (G.g_realm )
+@@ -956,8 +963,11 @@ static void send_headers(int responseNum
+ }
+ #endif
+ if (responseNum == HTTP_MOVED_TEMPORARILY) {
+- len += sprintf(iobuf + len, "Location: %s/%s%s\r\n",
++ len += sprintf(iobuf + len, "Location: %s%s%s%s%s%s\r\n",
++ (redirect_host ? "http://" : ""),
++ (redirect_host ? redirect_host : ""),
+ found_moved_temporarily,
++ (redirect_host ? "" : "/"),
+ (g_query ? "?" : ""),
+ (g_query ? g_query : ""));
+ }
+@@ -1925,8 +1935,12 @@ static void handle_incoming_and_exit(con
+ } while (*++tptr);
+ *++urlp = '\0'; /* terminate after last character */
+
++ /* redirect active */
++ if (redirect_path && (strncmp(urlcopy, redirect_path, strlen(redirect_path)) != 0))
++ found_moved_temporarily = redirect_path;
++
+ /* If URL is a directory, add '/' */
+- if (urlp[-1] != '/') {
++ if (!redirect_path && (urlp[-1] != '/')) {
+ if (is_directory(urlcopy + 1, 1, NULL)) {
+ found_moved_temporarily = urlcopy;
+ }
+@@ -2283,7 +2297,9 @@ static void sighup_handler(int sig UNUSE
+ }
+
+ enum {
+- c_opt_config_file = 0,
++ R_opt_redirect_path = 0,
++ H_opt_redirect_host,
++ c_opt_config_file,
+ d_opt_decode_url,
+ h_opt_home_httpd,
+ IF_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
+@@ -2332,12 +2348,13 @@ int httpd_main(int argc UNUSED_PARAM, ch
+ /* We do not "absolutize" path given by -h (home) opt.
+ * If user gives relative path in -h,
+ * $SCRIPT_FILENAME will not be set. */
+- opt = getopt32(argv, "c:d:h:"
++ opt = getopt32(argv, "R:H:c:d:h:"
+ IF_FEATURE_HTTPD_ENCODE_URL_STR("e:")
+ IF_FEATURE_HTTPD_BASIC_AUTH("r:")
+ IF_FEATURE_HTTPD_AUTH_MD5("m:")
+ IF_FEATURE_HTTPD_SETUID("u:")
+ "p:ifv",
++ &redirect_path, &redirect_host,
+ &opt_c_configFile, &url_for_decode, &home_httpd
+ IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
+ IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)
diff --git a/package/busybox/patches/410-httpd_cgi_headers.patch b/package/busybox/patches/410-httpd_cgi_headers.patch
new file mode 100644
index 000000000..b02a5b9d7
--- /dev/null
+++ b/package/busybox/patches/410-httpd_cgi_headers.patch
@@ -0,0 +1,16 @@
+--- a/networking/httpd.c
++++ b/networking/httpd.c
+@@ -1222,10 +1222,10 @@ static NOINLINE void cgi_io_loop_and_exi
+ if (full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
+ break;
+ }
+- /* Commented out:
+- if (!strstr(rbuf, "ontent-")) {
+- full_write(s, "Content-type: text/plain\r\n\r\n", 28);
++ if (!strstr(rbuf, "ontent-") && !strstr(rbuf, "ocation:")) {
++ full_write(1, "Content-type: text/plain\r\n\r\n", 28);
+ }
++ /* Previously commented out:
+ * Counter-example of valid CGI without Content-type:
+ * echo -en "HTTP/1.0 302 Found\r\n"
+ * echo -en "Location: http://www.busybox.net\r\n"
diff --git a/package/busybox/patches/440-httpd_chdir.patch b/package/busybox/patches/440-httpd_chdir.patch
new file mode 100644
index 000000000..401c6eb5d
--- /dev/null
+++ b/package/busybox/patches/440-httpd_chdir.patch
@@ -0,0 +1,10 @@
+--- a/networking/httpd.c
++++ b/networking/httpd.c
+@@ -1814,6 +1814,7 @@ static void handle_incoming_and_exit(con
+ char *header_ptr = header_ptr;
+ Htaccess_Proxy *proxy_entry;
+ #endif
++ xchdir(home_httpd);
+
+ /* Allocation of iobuf is postponed until now
+ * (IOW, server process doesn't need to waste 8k) */
diff --git a/package/busybox/patches/470-insmod_search.patch b/package/busybox/patches/470-insmod_search.patch
new file mode 100644
index 000000000..7f0188fc8
--- /dev/null
+++ b/package/busybox/patches/470-insmod_search.patch
@@ -0,0 +1,137 @@
+--- a/modutils/Config.src
++++ b/modutils/Config.src
+@@ -247,7 +247,7 @@ config FEATURE_MODUTILS_SYMBOLS
+ config DEFAULT_MODULES_DIR
+ string "Default directory containing modules"
+ default "/lib/modules"
+- depends on DEPMOD || MODPROBE || MODPROBE_SMALL || MODINFO
++ depends on DEPMOD || INSMOD || MODPROBE || MODPROBE_SMALL || MODINFO
+ help
+ Directory that contains kernel modules.
+ Defaults to "/lib/modules"
+--- a/modutils/insmod.c
++++ b/modutils/insmod.c
+@@ -11,6 +11,106 @@
+
+ #include "libbb.h"
+ #include "modutils.h"
++#include <sys/utsname.h>
++#ifndef CONFIG_FEATURE_2_4_MODULES
++#include <sys/mman.h>
++#include <asm/unistd.h>
++#include <sys/syscall.h>
++#endif
++
++static char *g_filename = NULL;
++
++static int FAST_FUNC check_module_name_match(const char *filename, struct stat *statbuf,
++ void *userdata, int depth)
++{
++ char *fullname = (char *) userdata;
++ char *tmp;
++
++ if (fullname[0] == '\0')
++ return FALSE;
++
++ tmp = bb_get_last_path_component_nostrip(filename);
++ if (strcmp(tmp, fullname) == 0) {
++ /* Stop searching if we find a match */
++ g_filename = xstrdup(filename);
++ return FALSE;
++ }
++
++ return TRUE;
++}
++
++static int find_module(char *filename)
++{
++ char *module_dir, real_module_dir[FILENAME_MAX];
++ int len, slen, ret = ENOENT, k_version;
++ struct utsname myuname;
++ const char *suffix = ".ko";
++ struct stat st;
++
++ /* check the kernel version */
++ if (uname(&myuname) != 0)
++ return EINVAL;
++
++ k_version = myuname.release[0] - '0';
++
++ if (k_version < 2 || k_version > 9)
++ return EINVAL;
++
++ if (k_version == 2) {
++ int k_patchlevel = myuname.release[2] - '0';
++ if (k_patchlevel <= 4)
++#if ENABLE_FEATURE_2_4_MODULES
++ suffix = ".o";
++#else
++ return EINVAL;
++#endif
++ }
++
++ len = strlen(filename);
++ slen = strlen(suffix);
++
++ /* check for suffix and absolute path first */
++ if ((len < slen + 2) || (strcmp(filename + len - slen, suffix) != 0)) {
++ filename = xasprintf("%s%s", filename, suffix);
++ } else {
++ filename = strdup(filename);
++ if ((stat(filename, &st) == 0) && S_ISREG(st.st_mode)) {
++ g_filename = filename;
++ return 0;
++ }
++ free(filename);
++ return ENOENT;
++ }
++
++ /* next: scan /lib/modules/<release> */
++ /* Jump through hoops in case /lib/modules/`uname -r`
++ * is a symlink. We do not want recursive_action to
++ * follow symlinks, but we do want to follow the
++ * /lib/modules/`uname -r` dir, So resolve it ourselves
++ * if it is a link... */
++ module_dir = concat_path_file(CONFIG_DEFAULT_MODULES_DIR, myuname.release);
++ if (realpath(module_dir, real_module_dir) != NULL) {
++ free(module_dir);
++ module_dir = real_module_dir;
++ }
++
++ recursive_action(module_dir, ACTION_RECURSE,
++ check_module_name_match, 0, filename, 0);
++
++ /* Check if we have a complete path */
++ if (g_filename == NULL)
++ goto done;
++
++ if ((stat(g_filename, &st) == 0) && S_ISREG(st.st_mode))
++ ret = 0;
++ else
++ free(g_filename);
++
++done:
++ free(filename);
++
++ return ret;
++}
+
+ /* 2.6 style insmod has no options and required filename
+ * (not module name - .ko can't be omitted) */
+@@ -58,9 +158,15 @@ int insmod_main(int argc UNUSED_PARAM, c
+ if (!filename)
+ bb_show_usage();
+
+- rc = bb_init_module(filename, parse_cmdline_module_options(argv, /*quote_spaces:*/ 0));
++ rc = find_module(filename);
++ if (rc || (g_filename == NULL))
++ goto done;
++
++ rc = bb_init_module(g_filename, parse_cmdline_module_options(argv, /*quote_spaces:*/ 0));
+ if (rc)
+ bb_error_msg("can't insert '%s': %s", filename, moderror(rc));
++ free (g_filename);
+
++done:
+ return rc;
+ }
diff --git a/package/busybox/patches/480-mount_union.patch b/package/busybox/patches/480-mount_union.patch
new file mode 100644
index 000000000..2f236a152
--- /dev/null
+++ b/package/busybox/patches/480-mount_union.patch
@@ -0,0 +1,29 @@
+--- a/util-linux/mount.c
++++ b/util-linux/mount.c
+@@ -136,6 +136,10 @@
+ # include <rpc/pmap_clnt.h>
+ #endif
+
++#ifndef MS_UNION
++#define MS_UNION 0x100 /* 256: Mount on top of a union */
++#endif
++
+
+ #if defined(__dietlibc__)
+ // 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
+@@ -254,6 +258,7 @@ static const int32_t mount_options[] = {
+ /* "rslave" */ MS_SLAVE|MS_RECURSIVE,
+ /* "rprivate" */ MS_PRIVATE|MS_RECURSIVE,
+ /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
++ /* "union" */ MS_UNION,
+ )
+
+ // Always understood.
+@@ -310,6 +315,7 @@ static const char mount_option_str[] =
+ "make-rslave\0"
+ "make-rprivate\0"
+ "make-runbindable\0"
++ "make-union\0"
+ )
+
+ // Always understood.
diff --git a/package/busybox/patches/490-mount_disable_check.patch b/package/busybox/patches/490-mount_disable_check.patch
new file mode 100644
index 000000000..2fcb7f7fc
--- /dev/null
+++ b/package/busybox/patches/490-mount_disable_check.patch
@@ -0,0 +1,12 @@
+--- a/util-linux/mount.c
++++ b/util-linux/mount.c
+@@ -128,9 +128,6 @@
+ #if ENABLE_FEATURE_MOUNT_NFS
+ /* This is just a warning of a common mistake. Possibly this should be a
+ * uclibc faq entry rather than in busybox... */
+-# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
+-# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
+-# endif
+ # include <rpc/rpc.h>
+ # include <rpc/pmap_prot.h>
+ # include <rpc/pmap_clnt.h>
diff --git a/package/busybox/patches/510-awk_include.patch b/package/busybox/patches/510-awk_include.patch
new file mode 100644
index 000000000..9f525acf8
--- /dev/null
+++ b/package/busybox/patches/510-awk_include.patch
@@ -0,0 +1,217 @@
+--- a/editors/awk.c
++++ b/editors/awk.c
+@@ -1,3 +1,4 @@
++
+ /* vi: set sw=4 ts=4: */
+ /*
+ * awk implementation for busybox
+@@ -81,9 +82,14 @@ typedef struct chain_s {
+ } chain;
+
+ /* Function */
++typedef var *(*awk_cfunc)(var *res, var *args, int nargs);
+ typedef struct func_s {
+ unsigned nargs;
++ enum { AWKFUNC, CFUNC } type;
++ union {
++ awk_cfunc cfunc;
+ struct chain_s body;
++ } x;
+ } func;
+
+ /* I/O stream */
+@@ -1473,7 +1479,8 @@ static void parse_program(char *p)
+ next_token(TC_FUNCTION);
+ g_pos++;
+ f = newfunc(t_string);
+- f->body.first = NULL;
++ f->type = AWKFUNC;
++ f->x.body.first = NULL;
+ f->nargs = 0;
+ while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) {
+ v = findvar(ahash, t_string);
+@@ -1482,7 +1489,7 @@ static void parse_program(char *p)
+ if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM)
+ break;
+ }
+- seq = &f->body;
++ seq = &f->x.body;
+ chain_group();
+ clear_array(ahash);
+
+@@ -2580,7 +2587,8 @@ static var *evaluate(node *op, var *res)
+ var *vbeg, *v;
+ const char *sv_progname;
+
+- if (!op->r.f->body.first)
++ if ((op->r.f->type == AWKFUNC) &&
++ !op->r.f->x.body.first)
+ syntax_error(EMSG_UNDEF_FUNC);
+
+ vbeg = v = nvalloc(op->r.f->nargs + 1);
+@@ -2597,7 +2605,10 @@ static var *evaluate(node *op, var *res)
+ fnargs = vbeg;
+ sv_progname = g_progname;
+
+- res = evaluate(op->r.f->body.first, res);
++ if (op->r.f->type == AWKFUNC)
++ res = evaluate(op->r.f->x.body.first, res);
++ else if (op->r.f->type == CFUNC)
++ res = op->r.f->x.cfunc(res, fnargs, op->r.f->nargs);
+
+ g_progname = sv_progname;
+ nvfree(fnargs);
+@@ -2991,6 +3002,143 @@ static rstream *next_input_file(void)
+ #undef files_happen
+ }
+
++/* read the contents of an entire file */
++static char *get_file(const char *fname)
++{
++ FILE *F;
++ char *s = NULL;
++ int i, j, flen;
++
++ F = fopen(fname, "r");
++ if (!F) {
++ return NULL;
++ }
++
++ if (fseek(F, 0, SEEK_END) == 0) {
++ flen = ftell(F);
++ s = (char *)xmalloc(flen+4);
++ fseek(F, 0, SEEK_SET);
++ i = 1 + fread(s+1, 1, flen, F);
++ } else {
++ for (i=j=1; j>0; i+=j) {
++ s = (char *)xrealloc(s, i+4096);
++ j = fread(s+i, 1, 4094, F);
++ }
++ }
++
++ s[i] = '\0';
++ fclose(F);
++ return s;
++}
++
++
++/* parse_include():
++ *
++ * taken from parse_program from awk.c
++ * END{} is not parsed here, and BEGIN{} is executed immediately
++ */
++static void parse_include(char *p)
++{
++ uint32_t tclass;
++ chain *initseq = NULL;
++ chain tmp;
++ func *f;
++ var *v, *tv;
++
++ tv = nvalloc(1);
++ memset(&tmp, 0, sizeof(tmp));
++ g_pos = p;
++ t_lineno = 1;
++ while ((tclass = next_token(TC_EOF | TC_OPSEQ |
++ TC_OPTERM | TC_BEGIN | TC_FUNCDECL)) != TC_EOF) {
++ if (tclass & TC_OPTERM)
++ continue;
++
++ seq = &tmp;
++ if (tclass & TC_BEGIN) {
++ initseq = xzalloc(sizeof(chain));
++ seq = initseq;
++ chain_group();
++ } else if (tclass & TC_FUNCDECL) {
++ next_token(TC_FUNCTION);
++ g_pos++;
++ f = newfunc(t_string);
++ f->type = AWKFUNC;
++ f->x.body.first = NULL;
++ f->nargs = 0;
++ while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) {
++ v = findvar(ahash, t_string);
++ v->x.aidx = (f->nargs)++;
++
++ if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM)
++ break;
++ }
++ seq = &(f->x.body);
++ chain_group();
++ clear_array(ahash);
++ }
++ }
++ if (initseq && initseq->first)
++ tv = evaluate(initseq->first, tv);
++ nvfree(tv);
++}
++
++
++/* include an awk file and run its BEGIN{} section */
++static xhash *includes = NULL;
++static void include_file(const char *filename)
++{
++ char *s;
++ var *v;
++ int oldlnr = g_lineno;
++ const char *oldprg = g_progname;
++
++ if (!includes)
++ includes = hash_init();
++
++ /* find out if the file has been included already */
++ v = findvar(includes, filename);
++ if (istrue(v))
++ return;
++ setvar_s(v, "1");
++
++ /* read include file */
++ s = get_file(filename);
++ if (!s) {
++ fprintf(stderr, "Could not open file.\n");
++ return;
++ }
++ g_lineno = 1;
++ g_progname = xstrdup(filename);
++ parse_include(s+1);
++ free(s);
++ g_lineno = oldlnr;
++ g_progname = oldprg;
++}
++
++static var *include(var *res, var *args, int nargs)
++{
++ const char *s;
++
++ nargs = nargs; /* shut up, gcc */
++ s = getvar_s(args);
++ if (s && (strlen(s) > 0))
++ include_file(s);
++
++ return res;
++}
++
++/* registers a global c function for the awk interpreter */
++static void register_cfunc(const char *name, awk_cfunc cfunc, int nargs)
++{
++ func *f;
++
++ f = newfunc(name);
++ f->type = CFUNC;
++ f->x.cfunc = cfunc;
++ f->nargs = nargs;
++}
++
+ int awk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+ int awk_main(int argc, char **argv)
+ {
+@@ -3056,6 +3204,9 @@ int awk_main(int argc, char **argv)
+ *s1 = '=';
+ }
+ }
++
++ register_cfunc("include", include, 1);
++
+ opt_complementary = "v::f::"; /* -v and -f can occur multiple times */
+ opt = getopt32(argv, "F:v:f:W:", &opt_F, &list_v, &list_f, &opt_W);
+ argv += optind;
diff --git a/package/busybox/patches/524-udhcpc_renew.patch b/package/busybox/patches/524-udhcpc_renew.patch
new file mode 100644
index 000000000..01edda3a5
--- /dev/null
+++ b/package/busybox/patches/524-udhcpc_renew.patch
@@ -0,0 +1,10 @@
+--- a/networking/udhcp/dhcpc.c
++++ b/networking/udhcp/dhcpc.c
+@@ -983,7 +983,6 @@ static void perform_renew(void)
+ state = RENEW_REQUESTED;
+ break;
+ case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
+- udhcp_run_script(NULL, "deconfig");
+ case REQUESTING:
+ case RELEASED:
+ change_listen_mode(LISTEN_RAW);
diff --git a/package/busybox/patches/610-ntpd_delayed_resolve.patch b/package/busybox/patches/610-ntpd_delayed_resolve.patch
new file mode 100644
index 000000000..5e17ae16c
--- /dev/null
+++ b/package/busybox/patches/610-ntpd_delayed_resolve.patch
@@ -0,0 +1,48 @@
+--- a/networking/ntpd.c
++++ b/networking/ntpd.c
+@@ -216,6 +216,7 @@ typedef struct {
+ typedef struct {
+ len_and_sockaddr *p_lsa;
+ char *p_dotted;
++ char *p_hostname;
+ /* when to send new query (if p_fd == -1)
+ * or when receive times out (if p_fd >= 0): */
+ int p_fd;
+@@ -646,8 +647,9 @@ add_peers(char *s)
+ peer_t *p;
+
+ p = xzalloc(sizeof(*p));
+- p->p_lsa = xhost2sockaddr(s, 123);
+- p->p_dotted = xmalloc_sockaddr2dotted_noport(&p->p_lsa->u.sa);
++ p->p_hostname = s;
++ p->p_lsa = NULL;
++ p->p_dotted = NULL;
+ p->p_fd = -1;
+ p->p_xmt_msg.m_status = MODE_CLIENT | (NTP_VERSION << 3);
+ p->next_action_time = G.cur_time; /* = set_next(p, 0); */
+@@ -696,6 +698,25 @@ send_query_to_peer(peer_t *p)
+ *
+ * Uncomment this and use strace to see it in action:
+ */
++
++ /* See if the peer hostname already resolved yet, if not, retry to resolv and return on failure */
++ if (!p->p_lsa)
++ {
++ p->p_lsa = host2sockaddr(p->p_hostname, 123);
++
++ if (p->p_lsa)
++ {
++ p->p_dotted = xmalloc_sockaddr2dotted_noport(&p->p_lsa->u.sa);
++ VERB1 bb_error_msg("resolved peer %s to %s", p->p_hostname, p->p_dotted);
++ }
++ else
++ {
++ set_next(p, RETRY_INTERVAL);
++ VERB1 bb_error_msg("could not resolve peer %s, skipping", p->p_hostname);
++ return;
++ }
++ }
++
+ #define PROBE_LOCAL_ADDR /* { len_and_sockaddr lsa; lsa.len = LSA_SIZEOF_SA; getsockname(p->query.fd, &lsa.u.sa, &lsa.len); } */
+
+ if (p->p_fd == -1) {
diff --git a/package/busybox/patches/700-hexdump_segfault_fix.patch b/package/busybox/patches/700-hexdump_segfault_fix.patch
new file mode 100644
index 000000000..ab09fb3e4
--- /dev/null
+++ b/package/busybox/patches/700-hexdump_segfault_fix.patch
@@ -0,0 +1,11 @@
+--- a/libbb/dump.c
++++ b/libbb/dump.c
+@@ -301,7 +301,7 @@ static NOINLINE void rewrite(priv_dumper
+ ) {
+ fu->reps += (dumper->blocksize - fs->bcnt) / fu->bcnt;
+ }
+- if (fu->reps > 1) {
++ if (fu->reps > 1 && fu->nextpr) {
+ for (pr = fu->nextpr;; pr = pr->nextpr)
+ if (!pr->nextpr)
+ break;
diff --git a/package/busybox/patches/802-brctl_linux24.patch b/package/busybox/patches/802-brctl_linux24.patch
new file mode 100644
index 000000000..8cc19779a
--- /dev/null
+++ b/package/busybox/patches/802-brctl_linux24.patch
@@ -0,0 +1,74 @@
+--- a/networking/brctl.c
++++ b/networking/brctl.c
+@@ -56,7 +56,7 @@
+
+ /* Maximum number of ports supported per bridge interface. */
+ #ifndef MAX_PORTS
+-# define MAX_PORTS 32
++# define MAX_PORTS 1024
+ #endif
+
+ /* Use internal number parsing and not the "exact" conversion. */
+@@ -193,6 +193,7 @@ int brctl_main(int argc UNUSED_PARAM, ch
+ printf(bi.stp_enabled ? "\tyes" : "\tno");
+
+ /* print interface list */
++ memset(ifidx, 0, sizeof ifidx);
+ arm_ioctl(args, BRCTL_GET_PORT_LIST,
+ (unsigned long) ifidx, MAX_PORTS);
+ xioctl(fd, SIOCDEVPRIVATE, &ifr);
+@@ -221,9 +222,19 @@ int brctl_main(int argc UNUSED_PARAM, ch
+ br = *argv++;
+
+ if (key == ARG_addbr || key == ARG_delbr) { /* addbr or delbr */
+- ioctl_or_perror_and_die(fd,
+- key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
+- br, "bridge %s", br);
++ int ret;
++ ret = ioctl(fd,
++ key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
++ br);
++ if (ret < 0) {
++ arm_ioctl(args,
++ key == ARG_addbr ? BRCTL_ADD_BRIDGE : BRCTL_DEL_BRIDGE,
++ (unsigned long) br, 0);
++ ret = ioctl(fd, SIOCSIFBR, args);
++ }
++ if (ret < 0) {
++ bb_perror_msg_and_die("bridge %s", br);
++ }
+ goto done;
+ }
+
+@@ -232,14 +243,27 @@ int brctl_main(int argc UNUSED_PARAM, ch
+
+ strncpy_IFNAMSIZ(ifr.ifr_name, br);
+ if (key == ARG_addif || key == ARG_delif) { /* addif or delif */
++ int ret;
++ int if_index;
+ brif = *argv;
+- ifr.ifr_ifindex = if_nametoindex(brif);
+- if (!ifr.ifr_ifindex) {
++ if_index = if_nametoindex(brif);
++ if (!if_index) {
+ bb_perror_msg_and_die("iface %s", brif);
+ }
+- ioctl_or_perror_and_die(fd,
++ ifr.ifr_ifindex = if_index;
++ ret = ioctl(fd,
+ key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
+- &ifr, "bridge %s", br);
++ &ifr);
++ if (ret < 0) {
++ arm_ioctl(args,
++ key == ARG_addif ? BRCTL_ADD_IF : BRCTL_DEL_IF,
++ if_index, 0);
++ ifr.ifr_data = (char *) &args;
++ ret = ioctl(fd, SIOCDEVPRIVATE, &ifr);
++ }
++ if (ret < 0) {
++ bb_perror_msg_and_die("bridge %s", br);
++ }
+ goto done_next_argv;
+ }
+ #if ENABLE_FEATURE_BRCTL_FANCY
diff --git a/package/busybox/patches/803-id_getgrouplist.patch b/package/busybox/patches/803-id_getgrouplist.patch
new file mode 100644
index 000000000..638305ee7
--- /dev/null
+++ b/package/busybox/patches/803-id_getgrouplist.patch
@@ -0,0 +1,49 @@
+This patch disable supplementary groups handling in id with uClibc versions
+prior to v0.9.30, which are lacking the 'getgrouplist' function.
+
+Signed-off-by: Nicolas Thill <nico@openwrt.org>
+
+--- a/coreutils/id.c
++++ b/coreutils/id.c
+@@ -63,10 +63,12 @@
+
+ /* This is a NOEXEC applet. Be very careful! */
+
++#define HAVE_GETGROUPLIST 1
++
+ #if !ENABLE_USE_BB_PWD_GRP
+ #if defined(__UCLIBC_MAJOR__) && (__UCLIBC_MAJOR__ == 0)
+ #if (__UCLIBC_MINOR__ < 9) || (__UCLIBC_MINOR__ == 9 && __UCLIBC_SUBLEVEL__ < 30)
+-#error "Sorry, you need at least uClibc version 0.9.30 for id applet to build"
++#undef HAVE_GETGROUPLIST
+ #endif
+ #endif
+ #endif
+@@ -128,7 +130,11 @@ static int get_groups(const char *userna
+ /* If the user is a member of more than
+ * *n groups, then -1 is returned. Otherwise >= 0.
+ * (and no defined way of detecting errors?!) */
++#if HAVE_GETGROUPLIST
+ m = getgrouplist(username, rgid, groups, n);
++#else
++ *n = -1;
++#endif
+ /* I guess *n < 0 might indicate error. Anyway,
+ * malloc'ing -1 bytes won't be good, so: */
+ if (*n < 0)
+@@ -210,6 +216,7 @@ int id_main(int argc UNUSED_PARAM, char
+ if (egid != rgid)
+ status |= print_group(egid, " ");
+ }
++#if HAVE_GETGROUPLIST
+ /* We are supplying largish buffer, trying
+ * to not run get_groups() twice. That might be slow
+ * ("user database in remote SQL server" case) */
+@@ -236,6 +243,7 @@ int id_main(int argc UNUSED_PARAM, char
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(groups);
++#endif
+ #if ENABLE_SELINUX
+ if (is_selinux_enabled()) {
+ if (getcon(&scontext) == 0)
diff --git a/package/busybox/patches/902-telnetd_intr.patch b/package/busybox/patches/902-telnetd_intr.patch
new file mode 100644
index 000000000..862ece055
--- /dev/null
+++ b/package/busybox/patches/902-telnetd_intr.patch
@@ -0,0 +1,10 @@
+--- a/networking/telnetd.c
++++ b/networking/telnetd.c
+@@ -330,6 +330,7 @@ make_new_session(
+
+ /* Restore default signal handling ASAP */
+ bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
++ signal(SIGINT, SIG_DFL);
+
+ pid = getpid();
+
diff --git a/package/busybox/patches/910-insmod-q-flag.patch b/package/busybox/patches/910-insmod-q-flag.patch
new file mode 100644
index 000000000..6002f20f1
--- /dev/null
+++ b/package/busybox/patches/910-insmod-q-flag.patch
@@ -0,0 +1,33 @@
+--- a/modutils/insmod.c
++++ b/modutils/insmod.c
+@@ -139,7 +139,7 @@ int insmod_main(int argc, char **argv) M
+ int insmod_main(int argc UNUSED_PARAM, char **argv)
+ {
+ char *filename;
+- int rc;
++ int rc, opt;
+
+ /* Compat note:
+ * 2.6 style insmod has no options and required filename
+@@ -149,10 +149,8 @@ int insmod_main(int argc UNUSED_PARAM, c
+ * or in $MODPATH.
+ */
+
+- IF_FEATURE_2_4_MODULES(
+- getopt32(argv, INSMOD_OPTS INSMOD_ARGS);
+- argv += optind - 1;
+- );
++ opt = getopt32(argv, INSMOD_OPTS, NULL, NULL);
++ argv += optind - 1;
+
+ filename = *++argv;
+ if (!filename)
+@@ -163,7 +161,7 @@ int insmod_main(int argc UNUSED_PARAM, c
+ goto done;
+
+ rc = bb_init_module(g_filename, parse_cmdline_module_options(argv, /*quote_spaces:*/ 0));
+- if (rc)
++ if (rc && !(opt & INSMOD_OPT_SILENT))
+ bb_error_msg("can't insert '%s': %s", filename, moderror(rc));
+ free (g_filename);
+
diff --git a/package/busybox/patches/911-date-k-flag.patch b/package/busybox/patches/911-date-k-flag.patch
new file mode 100644
index 000000000..c4f6db4db
--- /dev/null
+++ b/package/busybox/patches/911-date-k-flag.patch
@@ -0,0 +1,83 @@
+--- a/coreutils/date.c
++++ b/coreutils/date.c
+@@ -123,6 +123,7 @@
+ //usage: IF_FEATURE_DATE_ISOFMT(
+ //usage: "\n -D FMT Use FMT for -d TIME conversion"
+ //usage: )
++//usage: "\n -k Set Kernel timezone from localtime and exit"
+ //usage: "\n"
+ //usage: "\nRecognized TIME formats:"
+ //usage: "\n hh:mm[:ss]"
+@@ -135,6 +136,7 @@
+ //usage: "Wed Apr 12 18:52:41 MDT 2000\n"
+
+ #include "libbb.h"
++#include <sys/time.h>
+ #if ENABLE_FEATURE_DATE_NANO
+ # include <sys/syscall.h>
+ #endif
+@@ -145,8 +147,9 @@ enum {
+ OPT_UTC = (1 << 2), /* u */
+ OPT_DATE = (1 << 3), /* d */
+ OPT_REFERENCE = (1 << 4), /* r */
+- OPT_TIMESPEC = (1 << 5) * ENABLE_FEATURE_DATE_ISOFMT, /* I */
+- OPT_HINT = (1 << 6) * ENABLE_FEATURE_DATE_ISOFMT, /* D */
++ OPT_KERNELTZ = (1 << 5), /* k */
++ OPT_TIMESPEC = (1 << 6) * ENABLE_FEATURE_DATE_ISOFMT, /* I */
++ OPT_HINT = (1 << 7) * ENABLE_FEATURE_DATE_ISOFMT, /* D */
+ };
+
+ static void maybe_set_utc(int opt)
+@@ -164,12 +167,15 @@ static const char date_longopts[] ALIGN1
+ /* "universal\0" No_argument "u" */
+ "date\0" Required_argument "d"
+ "reference\0" Required_argument "r"
++ "set-kernel-tz\0" No_argument "k"
+ ;
+ #endif
+
+ int date_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+ int date_main(int argc UNUSED_PARAM, char **argv)
+ {
++ time_t tt;
++ struct timezone tz;
+ struct timespec ts;
+ struct tm tm_time;
+ char buf_fmt_dt2str[64];
+@@ -184,7 +190,7 @@ int date_main(int argc UNUSED_PARAM, cha
+ opt_complementary = "d--s:s--d"
+ IF_FEATURE_DATE_ISOFMT(":R--I:I--R");
+ IF_LONG_OPTS(applet_long_options = date_longopts;)
+- opt = getopt32(argv, "Rs:ud:r:"
++ opt = getopt32(argv, "Rs:ud:r:k"
+ IF_FEATURE_DATE_ISOFMT("I::D:"),
+ &date_str, &date_str, &filename
+ IF_FEATURE_DATE_ISOFMT(, &isofmt_arg, &fmt_str2dt));
+@@ -241,6 +247,27 @@ int date_main(int argc UNUSED_PARAM, cha
+ if (*argv)
+ bb_show_usage();
+
++ /* Setting of kernel timezone was requested */
++ if (opt & OPT_KERNELTZ) {
++ tt = time(NULL);
++ localtime_r(&tt, &tm_time);
++
++ /* workaround warp_clock() on first invocation */
++ memset(&tz, 0, sizeof(tz));
++ settimeofday(NULL, &tz);
++
++ memset(&tz, 0, sizeof(tz));
++ tz.tz_minuteswest = -(tm_time.tm_gmtoff / 60);
++
++ if (settimeofday(NULL, &tz))
++ {
++ bb_perror_msg("can't set kernel time zone");
++ return EXIT_FAILURE;
++ }
++
++ return EXIT_SUCCESS;
++ }
++
+ /* Now we have parsed all the information except the date format
+ * which depends on whether the clock is being set or read */
+
diff --git a/package/busybox/patches/920-portability.patch b/package/busybox/patches/920-portability.patch
new file mode 100644
index 000000000..4e5b9a057
--- /dev/null
+++ b/package/busybox/patches/920-portability.patch
@@ -0,0 +1,13 @@
+--- a/scripts/kconfig/mconf.c
++++ b/scripts/kconfig/mconf.c
+@@ -25,6 +25,10 @@
+ #include <unistd.h>
+ #include <locale.h>
+
++#ifndef SIGWINCH
++#define SIGWINCH 28
++#endif
++
+ #define LKC_DIRECT_LINK
+ #include "lkc.h"
+
diff --git a/package/busybox/patches/950-partial-checksum.patch b/package/busybox/patches/950-partial-checksum.patch
new file mode 100644
index 000000000..e595162c6
--- /dev/null
+++ b/package/busybox/patches/950-partial-checksum.patch
@@ -0,0 +1,87 @@
+diff -ur busybox-1.19.4.orig/networking/udhcp/dhcpc.c busybox-1.19.4/networking/udhcp/dhcpc.c
+--- busybox-1.19.4.orig/networking/udhcp/dhcpc.c 2012-09-06 22:33:53.476998721 +0400
++++ busybox-1.19.4/networking/udhcp/dhcpc.c 2012-09-07 01:09:46.693372304 +0400
+@@ -26,8 +26,8 @@
+ #include "dhcpc.h"
+
+ #include <netinet/if_ether.h>
+-#include <netpacket/packet.h>
+ #include <linux/filter.h>
++#include <linux/if_packet.h>
+
+ /* struct client_config_t client_config is in bb_common_bufsiz1 */
+
+@@ -784,17 +784,41 @@
+ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
+ {
+ int bytes;
++ int nocsum = 0;
+ struct ip_udp_dhcp_packet packet;
+ uint16_t check;
++ unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
++ struct iovec iov = {
++ .iov_base = &packet,
++ .iov_len = sizeof(packet),
++ };
++ struct msghdr msg = {
++ .msg_iov = &iov,
++ .msg_iovlen = 1,
++ .msg_control = cmsgbuf,
++ .msg_controllen = sizeof(cmsgbuf),
++ };
++ struct cmsghdr *cmsg;
+
+ memset(&packet, 0, sizeof(packet));
+- bytes = safe_read(fd, &packet, sizeof(packet));
++ do {
++ bytes = recvmsg(fd, &msg, 0);
++ } while (bytes < 0 && errno == EINTR);
++
+ if (bytes < 0) {
+ log1("Packet read error, ignoring");
+ /* NB: possible down interface, etc. Caller should pause. */
+ return bytes; /* returns -1 */
+ }
+
++ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
++ if (cmsg->cmsg_level == SOL_PACKET &&
++ cmsg->cmsg_type == PACKET_AUXDATA) {
++ struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg);
++ nocsum = aux->tp_status & TP_STATUS_CSUMNOTREADY;
++ }
++ }
++
+ if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp))) {
+ log1("Packet is too short, ignoring");
+ return -2;
+@@ -834,7 +858,7 @@
+ packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
+ check = packet.udp.check;
+ packet.udp.check = 0;
+- if (check && check != udhcp_checksum(&packet, bytes)) {
++ if (!nocsum && check && check != udhcp_checksum(&packet, bytes)) {
+ log1("Packet with bad UDP checksum received, ignoring");
+ return -2;
+ }
+@@ -880,6 +904,7 @@
+ {
+ int fd;
+ struct sockaddr_ll sock;
++ int val;
+
+ /*
+ * Comment:
+@@ -946,6 +971,13 @@
+ log1("Attached filter to raw socket fd %d", fd); // log?
+ }
+
++ val = 1;
++ if (setsockopt(fd, SOL_PACKET, PACKET_AUXDATA, &val,
++ sizeof(val)) < 0) {
++ if (errno != ENOPROTOOPT)
++ log1("Failed to set auxiliary packet data for socket fd %d", fd);
++ }
++
+ log1("Created raw socket");
+
+ return fd;