317 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			317 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2017 Denys Vlasenko <vda.linux@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')
 |