ipconfig.c.txt 8.5 KB

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