[klibc] ipconfig: support setting the DHCP vendor class identifier.

Support setting the DHCP vendor class identifier.  The default is
"Linux ipconfig" for the standalone version and "Linux kinit" for kinit.

Thanks to Geert Stappers for testing and the README update.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
diff --git a/usr/kinit/ipconfig/README b/usr/kinit/ipconfig/README
index 46c03fa..5c8227a 100644
--- a/usr/kinit/ipconfig/README
+++ b/usr/kinit/ipconfig/README
@@ -3,11 +3,14 @@
 
 Usage:
 
-ipconfig [-c proto] [-d interface] [-n] [-p port] [-t timeout] [interface ...]
+ipconfig [-c proto] [-d interface] [-i identifier]
+	 [-n] [-p port] [-t timeout] [interface ...]
 
 -c proto	Use PROTO as the configuration protocol for all
 		interfaces, unless overridden by specific interfaces.
 -d interface	Either the name of an interface, or a long spec.
+-i identifier	DHCP vendor class identifier.  The default is
+		"Linux ipconfig".
 -n		Do nothing - just print the configuration that would
 		be performed.
 -p port		Send bootp/dhcp broadcasts from PORT, to PORT - 1.
diff --git a/usr/kinit/ipconfig/dhcp_proto.c b/usr/kinit/ipconfig/dhcp_proto.c
index 9a30660..833c27e 100644
--- a/usr/kinit/ipconfig/dhcp_proto.c
+++ b/usr/kinit/ipconfig/dhcp_proto.c
@@ -49,12 +49,17 @@
 	255,
 };
 
+/* Both iovecs below have to have the same structure, since dhcp_send()
+   pokes at the internals */
+#define DHCP_IOV_LEN 6
+
 static struct iovec dhcp_discover_iov[] = {
 	/* [0] = ip + udp header */
 	/* [1] = bootp header */
 	[2] = {dhcp_discover_hdr, sizeof(dhcp_discover_hdr)},
 	[3] = {dhcp_params, sizeof(dhcp_params)},
-	[4] = {dhcp_end, sizeof(dhcp_end)}
+	/* [4] = DHCP vendor class */
+	[5] = {dhcp_end, sizeof(dhcp_end)}
 };
 
 static struct iovec dhcp_request_iov[] = {
@@ -62,7 +67,8 @@
 	/* [1] = bootp header */
 	[2] = {dhcp_request_hdr, sizeof(dhcp_request_hdr)},
 	[3] = {dhcp_params, sizeof(dhcp_params)},
-	[4] = {dhcp_end, sizeof(dhcp_end)}
+	/* [4] = DHCP vendor class */
+	[5] = {dhcp_end, sizeof(dhcp_end)}
 };
 
 /*
@@ -146,7 +152,7 @@
 	return dhcp_parse(dev, &bootp, dhcp_options, ret);
 }
 
-static int dhcp_send(struct netdev *dev, struct iovec *vec, int len)
+static int dhcp_send(struct netdev *dev, struct iovec *vec)
 {
 	struct bootp_hdr bootp;
 
@@ -164,9 +170,12 @@
 	vec[1].iov_base	= &bootp;
 	vec[1].iov_len	= sizeof(struct bootp_hdr);
 
+	vec[4].iov_base = vendor_class_identifier;
+	vec[4].iov_len  = vendor_class_identifier_len;
+
 	DEBUG(("xid %08x secs %d ", bootp.xid, ntohs(bootp.secs)));
 
-	return packet_send(dev, vec, len);
+	return packet_send(dev, vec, DHCP_IOV_LEN);
 }
 
 /*
@@ -179,7 +188,7 @@
 
 	DEBUG(("-> dhcp discover "));
 
-	return dhcp_send(dev, dhcp_discover_iov, 5);
+	return dhcp_send(dev, dhcp_discover_iov);
 }
 
 /*
@@ -200,7 +209,7 @@
 
 	DEBUG(("-> dhcp request "));
 
-	return dhcp_send(dev, dhcp_request_iov, 5);
+	return dhcp_send(dev, dhcp_request_iov);
 }
 
 /*
diff --git a/usr/kinit/ipconfig/ipconfig.h b/usr/kinit/ipconfig/ipconfig.h
index e98b517..064d1d6 100644
--- a/usr/kinit/ipconfig/ipconfig.h
+++ b/usr/kinit/ipconfig/ipconfig.h
@@ -14,6 +14,9 @@
 extern uint16_t cfg_local_port;
 extern uint16_t cfg_remote_port;
 
+extern char vendor_class_identifier[];
+extern int vendor_class_identifier_len;
+
 int ipconfig_main(int argc, char *argv[]);
 uint32_t ipconfig_server_address(void *next);
 
diff --git a/usr/kinit/ipconfig/main.c b/usr/kinit/ipconfig/main.c
index 48b01a5..fb6109f 100644
--- a/usr/kinit/ipconfig/main.c
+++ b/usr/kinit/ipconfig/main.c
@@ -34,6 +34,10 @@
 static int configured;
 static int bringup_first = 0;
 
+/* DHCP vendor class identifier */
+char vendor_class_identifier[260];
+int vendor_class_identifier_len;
+
 struct state {
 	int state;
 	int restart_state;
@@ -679,6 +683,21 @@
 	return nauto;
 }
 
+static void set_vendor_identifier(const char *id)
+{
+	int len = strlen(id);
+	if (len >= 255) {
+		fprintf(stderr,
+			"%s: invalid vendor class identifier: "
+			"%s\n", progname, id);
+		longjmp(abort_buf, 1);
+	}
+	memcpy(vendor_class_identifier+2, id, len);
+	vendor_class_identifier[0] = 60;
+	vendor_class_identifier[1] = len;
+	vendor_class_identifier_len = len+2;
+}
+
 int main(int argc, char *argv[])
     __attribute__ ((weak, alias("ipconfig_main")));
 
@@ -699,8 +718,11 @@
 	if ((err = setjmp(abort_buf)))
 		return err;
 
+	/* Default vendor identifier */
+	set_vendor_identifier("Linux ipconfig");
+
 	do {
-		c = getopt(argc, argv, "c:d:onp:t:");
+		c = getopt(argc, argv, "c:d:i:onp:t:");
 		if (c == EOF)
 			break;
 
@@ -728,6 +750,9 @@
 				longjmp(abort_buf, 1);
 			}
 			break;
+		case 'i':
+			set_vendor_identifier(optarg);
+			break;
 		case 'o':
 			bringup_first = 1;
 			break;
diff --git a/usr/kinit/kinit.c b/usr/kinit/kinit.c
index a4be8bc..7c33718 100644
--- a/usr/kinit/kinit.c
+++ b/usr/kinit/kinit.c
@@ -41,12 +41,14 @@
 static int do_ipconfig(int argc, char *argv[])
 {
 	int i, a = 0;
-	char **args = alloca((argc + 1) * sizeof(char *));
+	char **args = alloca((argc + 3) * sizeof(char *));
 
 	if (!args)
 		return -1;
 
 	args[a++] = (char *)"IP-Config";
+	args[a++] = (char *)"-i";
+	args[a++] = (char *)"Linux kinit";
 
 	DEBUG(("Running ipconfig\n"));