udhcpc: support option 0x79 (static routes) (part of bug 341)

function                                             old     new   delta
xmalloc_optname_optval                                 -     583    +583
dhcp_option_strings                                  258     266      +8
dhcp_options                                          70      72      +2
len_of_option_as_string                               11      12      +1
dhcp_option_lengths                                   11      12      +1
udhcp_run_script                                    1187     669    -518
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 4/1 up/down: 595/-518)           Total: 77 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/networking/udhcp/options.c b/networking/udhcp/options.c
index 76d6e37..ddb8944 100644
--- a/networking/udhcp/options.c
+++ b/networking/udhcp/options.c
@@ -48,6 +48,7 @@
 #if ENABLE_FEATURE_UDHCP_RFC3397
 	{ OPTION_STR1035 | OPTION_LIST            , 0x77 }, /* search             */
 #endif
+	{ OPTION_STATIC_ROUTES                    , 0x79 }, /* DHCP_STATIC_ROUTES */
 	/* MSIE's "Web Proxy Autodiscovery Protocol" support */
 	{ OPTION_STRING                           , 0xfc }, /* wpad               */
 
@@ -97,6 +98,7 @@
 #if ENABLE_FEATURE_UDHCP_RFC3397
 	"search" "\0"
 #endif
+	"staticroutes" "\0" /* DHCP_STATIC_ROUTES  */
 	/* MSIE's "Web Proxy Autodiscovery Protocol" support */
 	"wpad" "\0"
 	;
@@ -116,6 +118,8 @@
 	[OPTION_S16] =     2,
 	[OPTION_U32] =     4,
 	[OPTION_S32] =     4,
+	/* Just like OPTION_STRING, we use minimum length here */
+	[OPTION_STATIC_ROUTES] = 5,
 };
 
 
diff --git a/networking/udhcp/options.h b/networking/udhcp/options.h
index 9e62431..aeed369 100644
--- a/networking/udhcp/options.h
+++ b/networking/udhcp/options.h
@@ -20,6 +20,7 @@
 	OPTION_S16,
 	OPTION_U32,
 	OPTION_S32,
+	OPTION_STATIC_ROUTES,
 };
 
 /* Client requests this option by default */
@@ -68,6 +69,7 @@
 #define DHCP_VENDOR             0x3c
 #define DHCP_CLIENT_ID          0x3d
 #define DHCP_FQDN               0x51
+#define DHCP_STATIC_ROUTES      0x79
 #define DHCP_END                0xFF
 /* Offsets in option byte sequence */
 #define OPT_CODE                0
diff --git a/networking/udhcp/script.c b/networking/udhcp/script.c
index 94dabf4..f315008 100644
--- a/networking/udhcp/script.c
+++ b/networking/udhcp/script.c
@@ -17,6 +17,7 @@
 static const uint8_t len_of_option_as_string[] = {
 	[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_STRING] =	1,
 #if ENABLE_FEATURE_UDHCP_RFC3397
 	[OPTION_STR1035] =	1,
@@ -51,7 +52,7 @@
 
 
 /* Create "opt_name=opt_value" string */
-static char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_option *type_p, const char *opt_name)
+static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_option *type_p, const char *opt_name)
 {
 	unsigned upper_length;
 	int len, type, optlen;
@@ -106,6 +107,50 @@
 			memcpy(dest, option, len);
 			dest[len] = '\0';
 			return ret;	 /* Short circuit this case */
+		case OPTION_STATIC_ROUTES: {
+			/* Option binary format:
+			 * mask [one byte, 0..32]
+			 * ip [big endian, 0..4 bytes depending on mask]
+			 * router [big endian, 4 bytes]
+			 * may be repeated
+			 *
+			 * We convert it to a string "IP/MASK ROUTER IP2/MASK2 ROUTER2"
+			 */
+			const char *pfx = "";
+
+			while (len >= 1 + 4) { /* mask + 0-byte ip + router */
+				uint32_t nip;
+				uint8_t *p;
+				unsigned mask;
+				int bytes;
+
+				mask = *option++;
+				if (mask > 32)
+					break;
+				len--;
+
+				nip = 0;
+				p = (void*) &nip;
+				bytes = (mask + 7) / 8; /* 0 -> 0, 1..8 -> 1, 9..16 -> 2 etc */
+				while (--bytes >= 0) {
+					*p++ = *option++;
+					len--;
+				}
+				if (len < 4)
+					break;
+
+				/* print ip/mask */
+				dest += sprint_nip(dest, pfx, (void*) &nip);
+				pfx = " ";
+				dest += sprintf(dest, "/%u ", mask);
+				/* print router */
+				dest += sprint_nip(dest, "", option);
+				option += 4;
+				len -= 4;
+			}
+
+			return ret;
+		}
 #if ENABLE_FEATURE_UDHCP_RFC3397
 		case OPTION_STR1035:
 			/* unpack option into dest; use ret for prefix (i.e., "optname=") */