1/* 2 * Copyright (c) 2017 Denys Vlasenko <vda.linux@googlemail.com> 3 * 4 * Licensed under GPLv2, see file LICENSE in this source tree. 5 */ 6//config:config IPCONFIG 7//config: bool "ipconfig" 8//config: default y 9//config: help 10//config: (Auto)configure network. 11 12//applet:IF_IPCONFIG(APPLET(ipconfig, BB_DIR_BIN, BB_SUID_DROP)) 13 14//kbuild:lib-$(CONFIG_IPCONFIG) += ipconfig.o 15 16#include <net/if.h> 17#include "libbb.h" 18 19struct globals { 20 int fixed; 21 const char *hostname; 22}; 23#define G (*ptr_to_globals) 24#define INIT_G() do { \ 25 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 26} while (0) 27 28struct dev { 29 const char *name; 30 uint8_t fixed; 31 uint32_t ip_addr; 32 uint32_t ip_netmask; 33 uint32_t ip_server; 34 uint32_t ip_router; 35}; 36 37static int 38parse_method(const char *method) 39{ 40 int fixed; 41 42 fixed = (method[0] != '\0'); 43 if (fixed) { 44 /* if it's not "" */ 45 fixed = index_in_strings( 46 /* 0 */ "on""\0" 47 /* 1 */ "any""\0" 48 /* 2 */ "both""\0" 49 /* 3 */ "dhcp""\0" 50 /* 4 */ "bootp""\0" 51 /* 5 */ "rarp""\0" 52 /* 6 */ "none""\0" 53 /* 7 */ "static""\0" 54 /* 8 */ "off""\0" 55 , method 56 ); 57 if (fixed > 0) 58 fixed /= 6; 59 } 60 return fixed; 61} 62 63static uint32_t 64parse_addr(const char *ip) 65{ 66 struct in_addr in; 67 if (inet_aton(ip, &in) == 0) 68 bb_error_msg_and_die("bad IP address '%s'", ip); 69 return in.s_addr; 70} 71 72static struct dev* 73find_device(llist_t *iface_list, const char *name) 74{ 75 while (iface_list) { 76 struct dev *dev = (void*) iface_list->data; 77 if (strcmp(dev->name, name) == 0) 78 return dev; 79 iface_list = iface_list->link; 80 } 81 return NULL; 82} 83 84static void 85set_from_template(struct dev *dev, struct dev *template) 86{ 87 if (template->ip_addr != 0) 88 dev->ip_addr = template->ip_addr; 89 if (template->ip_netmask != 0) 90 dev->ip_netmask = template->ip_netmask; 91 if (template->ip_server != 0) 92 dev->ip_server = template->ip_server; 93 if (template->ip_router != 0) 94 dev->ip_router = template->ip_router; 95 dev->fixed = template->fixed; 96} 97 98// "ip=PROTO" - also implies -o 99// "nfsaddrs=PROTO" - also implies -o 100// "<devname>" 101// "[ip=/nfsaddrs=]IP:SERVER_IP:ROUTER:NETMASK:HOSTNAME:IFACE:METHOD" 102// all optional. trailing empty :: can be skipped, only one : needs to be there 103// (to distinguish from other formats). 104// ":::::eth0" - dhcp on eth0 105// ":" - dhcp on all ifaces 106// "::1.2.3.4" - dhcp on all ifaces, gateway is 1.2.3.4 (fairly nonsensical) 107static void 108add_all_devices(llist_t **iface_list, struct dev *template); 109static struct dev* 110add_device(llist_t **iface_list, char *ip) 111{ 112 struct dev *dev; 113 114 dev = xzalloc(sizeof(*dev)); 115 dev->fixed = G.fixed; 116 117 if (strncmp("ip=", ip, 3) == 0 118 || strncmp("nfsaddrs=", ip, 9) == 0 119 ) { 120 int fixed; 121 122 ip = strchr(ip, '=') + 1; 123 fixed = parse_method(ip); 124 if (fixed >= 0) { 125 add_all_devices(iface_list, dev); 126 free(dev); 127 return NULL; 128 } 129 } 130 131 if (!strchr(ip, ':')) { 132 dev->name = ip; 133 } else { 134 unsigned opt = 0; 135 while (ip && *ip) { 136 char *next = strchr(ip, ':'); 137 if (next) 138 *next++ = '\0'; 139 if (opt > 6) 140 bb_error_msg_and_die("too many options for %s", dev->name); 141 if (ip[0]) switch (opt) { 142 case 0: 143 dev->ip_addr = parse_addr(ip); 144 break; 145 case 1: 146 dev->ip_server = parse_addr(ip); 147 break; 148 case 2: 149 dev->ip_router = parse_addr(ip); 150 break; 151 case 3: 152 dev->ip_netmask = parse_addr(ip); 153 break; 154 case 4: 155 if (G.hostname && strcmp(G.hostname, ip) != 0) 156 bb_error_msg_and_die("hostname must be the same"); 157 G.hostname = ip; 158 break; 159 case 5: 160 dev->name = ip; 161 break; 162 case 6: 163 dev->fixed = parse_method(ip); 164 break; 165 } 166 ip = next; 167 opt++; 168 } 169 } 170 171 if (dev->name == NULL 172 || strcmp(dev->name, "all") == 0 173 ) { 174 add_all_devices(iface_list, dev); 175 free(dev); 176 return NULL; 177 } 178 llist_add_to_end(iface_list, dev); 179 return dev; 180} 181 182static void 183add_all_devices(llist_t **iface_list, struct dev *template) 184{ 185 DIR *d; 186 struct dirent *de; 187#define sys_class_net "/sys/class/net" 188 189 /* All forms of "config all ifaces" imply -o */ 190 option_mask32 |= 1; 191 192 d = opendir(sys_class_net); 193 if (!d) 194 return; 195 196 while ((de = readdir(d)) != NULL) { 197 struct dev *dev; 198 char *filename; 199 char p[sizeof(long)*3]; 200 unsigned long flags; 201 int r; 202 203 /* Exclude devices beginning with dots as well as . and .. */ 204 if (de->d_name[0] == '.') 205 continue; 206 filename = xasprintf("%s/%s/flags", sys_class_net, de->d_name); 207 r = open_read_close(filename, p, sizeof(p) - 1); 208 free(filename); 209 if (r < 0) 210 continue; 211 p[r] = '\0'; 212 /* file's format is "0xNNNN\n" */ 213 flags = bb_strtoul(p, NULL, 0); 214 /* 215 * Heuristic for if this is a reasonable boot interface. 216 * This is the same logic the in-kernel ipconfig uses. 217 */ 218 if (flags & IFF_LOOPBACK) 219 continue; 220 if (!(flags & (IFF_BROADCAST | IFF_POINTOPOINT))) 221 continue; 222 if (find_device(*iface_list, de->d_name)) 223 continue; 224 dev = add_device(iface_list, xstrdup(de->d_name)); 225 if (dev) 226 set_from_template(dev, template); 227 } 228 closedir(d); 229#undef sys_class_net 230} 231 232//usage:#define ipconfig_trivial_usage 233//usage: "[-c METHOD] [-t TIMEOUT] [-on] [-i VENDOR_ID] [-p PORT] [-d] IFACE..." 234//usage:#define ipconfig_full_usage "\n\n" 235//usage: "(Auto)configure network" 236//usage: "\n" 237//usage: "\n"" -c METHOD off/none/static or on/dhcp (default)" 238//usage: "\n"" -t SECONDS Give up after SECONDS" 239//usage: "\n"" -o Stop after one interface is configured" 240//usage: "\n"" -n Dry run" 241//usage: "\n"" -i VENDOR_ID DHCP vendor id (default '')" 242//usage: "\n"" -p PORT DHCP port to use" 243//usage: "\n"" [-d] IFACE... Interface(s)" 244//usage: "\n" 245//usage: "\n"" IFACE can be:" 246//usage: "\n"" all - configure all interfaces" 247//usage: "\n"" IFACE - configure this interface" 248//usage: "\n"" IP:SERVER_IP:ROUTER:NETMASK:HOSTNAME:IFACE:METHOD (all optional)" 249// TIMEOUT defaults to infinite 250// -d actually is an option with an argument 251// (not a clue why klibc-utils has two ways to specify interfaces) 252int ipconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 253int ipconfig_main(int argc UNUSED_PARAM, char **argv) 254{ 255 const char *method = ""; 256 const char *vendor_id = ""; 257 llist_t *devname_list = NULL; 258 llist_t *iface_list; 259 int timeout = -1; 260 unsigned port; 261 unsigned opt; 262 263 INIT_G(); 264 265 opt = getopt32(argv, 266 "onc:t:i:p:+d:*", 267 &method, &timeout, &vendor_id, &port, &devname_list 268 ); 269 argv += optind; 270 271 G.fixed = parse_method(method); 272 if (G.fixed < 0) 273 bb_show_usage(); 274 275 iface_list = NULL; 276 while (devname_list) 277 add_device(&iface_list, (char*) llist_pop(&devname_list)); 278 while (*argv) 279 add_device(&iface_list, *argv++); 280 281 while (iface_list) { 282 struct dev *dev = (void*) iface_list->data; 283 printf("name:'%s'\n", dev->name); 284 printf("fixed:%u\n" , dev->fixed); 285 printf("ip:%s/" , inet_ntoa(*(struct in_addr*)&dev->ip_addr)); 286 printf("%s\n" , inet_ntoa(*(struct in_addr*)&dev->ip_netmask)); 287 printf("server:%s\n", inet_ntoa(*(struct in_addr*)&dev->ip_server)); 288 printf("router:%s\n", inet_ntoa(*(struct in_addr*)&dev->ip_router)); 289 iface_list = iface_list->link; 290 } 291 bb_error_msg("hostname:'%s'", G.hostname); 292 bb_error_msg("fixed:%u", G.fixed); 293 294 return EXIT_SUCCESS; 295} 296//After device is configured, write out a "/run/net-IFACE.conf" file: 297// // udchcp env values: 298//write_option("DEVICE", dev->name); interface=eth0 299//write_option("PROTO", method); 300//write_option("IPV4ADDR", dev->ip_addr); ip=10.43.17.38 301//write_option("IPV4BROADCAST", dev->ip_broadcast); subnet=255.255.255.0 mask=24 302//write_option("IPV4NETMASK", dev->ip_netmask); subnet=255.255.255.0 mask=24 303//write_option("IPV4GATEWAY", dev->ip_gateway); router=10.43.17.254 304//write_option("IPV4DNS0", dev->ip_nameserver[0]); dns=10.38.5.26 10.11.5.19 305//write_option("IPV4DNS1", dev->ip_nameserver[1]); dns=10.38.5.26 10.11.5.19 306//write_option("HOSTNAME", dev->hostname); hostname="STR" 307//write_option("DNSDOMAIN", dev->dnsdomainname); domain=domain.com 308//write_option("NISDOMAIN", dev->nisdomainname); nisdomain="STR" 309//write_option("ROOTSERVER", my_inet_ntoa(dev->ip_server)); serverid=10.44.6.2 310//write_option("ROOTPATH", dev->bootpath); rootpath="STR" 311//write_option("filename", dev->filename); boot_file=/pxelinux.0 312//write_option("UPTIME", dev->uptime); sysinfo()->uptime 313//write_option("DHCPLEASETIME", dev->dhcpleasetime); lease=44148 314//write_option("DOMAINSEARCH", dev->domainsearch); search="ABC DEF" 315// 316//(write_option writes out single-quote escaped string, VAR='VAL') 317