[git commit] klibc-utils: add ipconfig.c work-in-progress

Denys Vlasenko vda.linux at googlemail.com
Fri Sep 1 10:48:15 UTC 2017


commit: https://git.busybox.net/busybox/commit/?id=e39da802dd6d3ccfb95865139f98b184db0e175b
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 klibc-utils/ipconfig.c.txt | 316 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 316 insertions(+)

diff --git a/klibc-utils/ipconfig.c.txt b/klibc-utils/ipconfig.c.txt
new file mode 100644
index 0000000..5dd95c1
--- /dev/null
+++ b/klibc-utils/ipconfig.c.txt
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2017 Denys Vlasenko <vda.linux at googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+//config:config IPCONFIG
+//config:	bool "ipconfig"
+//config:	default y
+//config:	help
+//config:	(Auto)configure network.
+
+//applet:IF_IPCONFIG(APPLET(ipconfig, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_IPCONFIG) += ipconfig.o
+
+#include <net/if.h>
+#include "libbb.h"
+
+struct globals {
+	int fixed;
+	const char *hostname;
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+struct dev {
+	const char *name;
+	uint8_t  fixed;
+	uint32_t ip_addr;
+	uint32_t ip_netmask;
+	uint32_t ip_server;
+	uint32_t ip_router;
+};
+
+static int
+parse_method(const char *method)
+{
+	int fixed;
+
+	fixed = (method[0] != '\0');
+	if (fixed) {
+		/* if it's not "" */
+		fixed = index_in_strings(
+			/* 0 */ "on""\0"
+			/* 1 */ "any""\0"
+			/* 2 */ "both""\0"
+			/* 3 */ "dhcp""\0"
+			/* 4 */ "bootp""\0"
+			/* 5 */ "rarp""\0"
+			/* 6 */ "none""\0"
+			/* 7 */ "static""\0"
+			/* 8 */ "off""\0"
+			, method
+		);
+		if (fixed > 0)
+			fixed /= 6;
+	}
+	return fixed;
+}
+
+static uint32_t
+parse_addr(const char *ip)
+{
+	struct in_addr in;
+	if (inet_aton(ip, &in) == 0)
+		bb_error_msg_and_die("bad IP address '%s'", ip);
+	return in.s_addr;
+}
+
+static struct dev*
+find_device(llist_t *iface_list, const char *name)
+{
+	while (iface_list) {
+		struct dev *dev = (void*) iface_list->data;
+		if (strcmp(dev->name, name) == 0)
+			return dev;
+		iface_list = iface_list->link;
+	}
+	return NULL;
+}
+
+static void
+set_from_template(struct dev *dev, struct dev *template)
+{
+	if (template->ip_addr != 0)
+		dev->ip_addr = template->ip_addr;
+	if (template->ip_netmask != 0)
+		dev->ip_netmask = template->ip_netmask;
+	if (template->ip_server != 0)
+		dev->ip_server = template->ip_server;
+	if (template->ip_router != 0)
+		dev->ip_router = template->ip_router;
+	dev->fixed = template->fixed;
+}
+
+// "ip=PROTO" - also implies -o
+// "nfsaddrs=PROTO" - also implies -o
+// "<devname>"
+// "[ip=/nfsaddrs=]IP:SERVER_IP:ROUTER:NETMASK:HOSTNAME:IFACE:METHOD"
+// all optional. trailing empty :: can be skipped, only one : needs to be there
+// (to distinguish from other formats).
+// ":::::eth0" - dhcp on eth0
+// ":" - dhcp on all ifaces
+// "::1.2.3.4" - dhcp on all ifaces, gateway is 1.2.3.4 (fairly nonsensical)
+static void
+add_all_devices(llist_t **iface_list, struct dev *template);
+static struct dev*
+add_device(llist_t **iface_list, char *ip)
+{
+	struct dev *dev;
+
+	dev = xzalloc(sizeof(*dev));
+	dev->fixed = G.fixed;
+
+	if (strncmp("ip=", ip, 3) == 0
+	 || strncmp("nfsaddrs=", ip, 9) == 0
+	) {
+		int fixed;
+
+		ip = strchr(ip, '=') + 1;
+		fixed = parse_method(ip);
+		if (fixed >= 0) {
+			add_all_devices(iface_list, dev);
+			free(dev);
+			return NULL;
+		}
+	}
+
+	if (!strchr(ip, ':')) {
+		dev->name = ip;
+	} else {
+		unsigned opt = 0;
+		while (ip && *ip) {
+			char *next = strchr(ip, ':');
+			if (next)
+				*next++ = '\0';
+			if (opt > 6)
+				bb_error_msg_and_die("too many options for %s", dev->name);
+			if (ip[0]) switch (opt) {
+			case 0:
+				dev->ip_addr = parse_addr(ip);
+				break;
+			case 1:
+				dev->ip_server = parse_addr(ip);
+				break;
+			case 2:
+				dev->ip_router = parse_addr(ip);
+				break;
+			case 3:
+				dev->ip_netmask = parse_addr(ip);
+				break;
+			case 4:
+				if (G.hostname && strcmp(G.hostname, ip) != 0)
+					bb_error_msg_and_die("hostname must be the same");
+				G.hostname = ip;
+				break;
+			case 5:
+				dev->name = ip;
+				break;
+			case 6:
+				dev->fixed = parse_method(ip);
+				break;
+			}
+			ip = next;
+			opt++;
+		}
+	}
+
+	if (dev->name == NULL
+	 || strcmp(dev->name, "all") == 0
+	) {
+		add_all_devices(iface_list, dev);
+		free(dev);
+		return NULL;
+	}
+	llist_add_to_end(iface_list, dev);
+	return dev;
+}
+
+static void
+add_all_devices(llist_t **iface_list, struct dev *template)
+{
+	DIR *d;
+	struct dirent *de;
+#define sys_class_net "/sys/class/net"
+
+	/* All forms of "config all ifaces" imply -o */
+	option_mask32 |= 1;
+
+	d = opendir(sys_class_net);
+	if (!d)
+		return;
+
+	while ((de = readdir(d)) != NULL) {
+		struct dev *dev;
+		char *filename;
+		char p[sizeof(long)*3];
+		unsigned long flags;
+		int r;
+
+		/* Exclude devices beginning with dots as well as . and .. */
+		if (de->d_name[0] == '.')
+			continue;
+		filename = xasprintf("%s/%s/flags", sys_class_net, de->d_name);
+		r = open_read_close(filename, p, sizeof(p) - 1);
+		free(filename);
+		if (r < 0)
+			continue;
+		p[r] = '\0';
+		/* file's format is "0xNNNN\n" */
+		flags = bb_strtoul(p, NULL, 0);
+		/*
+		 * Heuristic for if this is a reasonable boot interface.
+		 * This is the same logic the in-kernel ipconfig uses.
+		 */
+		if (flags & IFF_LOOPBACK)
+			continue;
+		if (!(flags & (IFF_BROADCAST | IFF_POINTOPOINT)))
+			continue;
+		if (find_device(*iface_list, de->d_name))
+			continue;
+		dev = add_device(iface_list, xstrdup(de->d_name));
+		if (dev)
+			set_from_template(dev, template);
+	}
+	closedir(d);
+#undef sys_class_net
+}
+
+//usage:#define ipconfig_trivial_usage
+//usage:       "[-c METHOD] [-t TIMEOUT] [-on] [-i VENDOR_ID] [-p PORT] [-d] IFACE..."
+//usage:#define ipconfig_full_usage "\n\n"
+//usage:       "(Auto)configure network"
+//usage:   "\n"
+//usage:   "\n""	-c METHOD	off/none/static or on/dhcp (default)"
+//usage:   "\n""	-t SECONDS	Give up after SECONDS"
+//usage:   "\n""	-o		Stop after one interface is configured"
+//usage:   "\n""	-n		Dry run"
+//usage:   "\n""	-i VENDOR_ID	DHCP vendor id (default '')"
+//usage:   "\n""	-p PORT		DHCP port to use"
+//usage:   "\n""	[-d] IFACE...	Interface(s)"
+//usage:   "\n"
+//usage:   "\n""	IFACE can be:"
+//usage:   "\n""	all - configure all interfaces"
+//usage:   "\n""	IFACE - configure this interface"
+//usage:   "\n""	IP:SERVER_IP:ROUTER:NETMASK:HOSTNAME:IFACE:METHOD (all optional)"
+// TIMEOUT defaults to infinite
+// -d actually is an option with an argument
+// (not a clue why klibc-utils has two ways to specify interfaces)
+int ipconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ipconfig_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *method = "";
+	const char *vendor_id = "";
+	llist_t *devname_list = NULL;
+	llist_t *iface_list;
+	int timeout = -1;
+	unsigned port;
+	unsigned opt;
+
+	INIT_G();
+
+	opt = getopt32(argv,
+		"onc:t:i:p:+d:*",
+		&method, &timeout, &vendor_id, &port, &devname_list
+	);
+	argv += optind;
+
+	G.fixed = parse_method(method);
+	if (G.fixed < 0)
+		bb_show_usage();
+
+	iface_list = NULL;
+	while (devname_list)
+		add_device(&iface_list, (char*) llist_pop(&devname_list));
+	while (*argv)
+		add_device(&iface_list, *argv++);
+
+	while (iface_list) {
+		struct dev *dev = (void*) iface_list->data;
+		printf("name:'%s'\n", dev->name);
+		printf("fixed:%u\n" , dev->fixed);
+		printf("ip:%s/"     , inet_ntoa(*(struct in_addr*)&dev->ip_addr));
+		printf("%s\n"       , inet_ntoa(*(struct in_addr*)&dev->ip_netmask));
+		printf("server:%s\n", inet_ntoa(*(struct in_addr*)&dev->ip_server));
+		printf("router:%s\n", inet_ntoa(*(struct in_addr*)&dev->ip_router));
+		iface_list = iface_list->link;
+	}
+	bb_error_msg("hostname:'%s'", G.hostname);
+	bb_error_msg("fixed:%u", G.fixed);
+
+	return EXIT_SUCCESS;
+}
+//After device is configured, write out a "/run/net-IFACE.conf" file:
+//                                                              // udchcp env values:
+//write_option("DEVICE",        dev->name);                     interface=eth0
+//write_option("PROTO",         method);
+//write_option("IPV4ADDR",      dev->ip_addr);                  ip=10.43.17.38
+//write_option("IPV4BROADCAST", dev->ip_broadcast);             subnet=255.255.255.0 mask=24
+//write_option("IPV4NETMASK",   dev->ip_netmask);               subnet=255.255.255.0 mask=24
+//write_option("IPV4GATEWAY",   dev->ip_gateway);               router=10.43.17.254
+//write_option("IPV4DNS0",      dev->ip_nameserver[0]);         dns=10.38.5.26 10.11.5.19
+//write_option("IPV4DNS1",      dev->ip_nameserver[1]);         dns=10.38.5.26 10.11.5.19
+//write_option("HOSTNAME",      dev->hostname);                   hostname="STR"
+//write_option("DNSDOMAIN",     dev->dnsdomainname);            domain=domain.com
+//write_option("NISDOMAIN",     dev->nisdomainname);              nisdomain="STR"
+//write_option("ROOTSERVER",    my_inet_ntoa(dev->ip_server));  serverid=10.44.6.2
+//write_option("ROOTPATH",      dev->bootpath);                   rootpath="STR"
+//write_option("filename",      dev->filename);                 boot_file=/pxelinux.0
+//write_option("UPTIME",        dev->uptime);                     sysinfo()->uptime
+//write_option("DHCPLEASETIME", dev->dhcpleasetime);            lease=44148
+//write_option("DOMAINSEARCH",  dev->domainsearch);             search="ABC DEF"
+//
+//(write_option writes out single-quote escaped string, VAR='VAL')


More information about the busybox-cvs mailing list