diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-09-01 12:48:15 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-09-01 12:48:15 +0200 |
commit | e39da802dd6d3ccfb95865139f98b184db0e175b (patch) | |
tree | 1aca6f1e7c0d60c3ca33f98994980071a8908fa1 | |
parent | 58bf902a94b5d26f546e3d4186d6f51e050be427 (diff) | |
download | busybox-w32-e39da802dd6d3ccfb95865139f98b184db0e175b.tar.gz busybox-w32-e39da802dd6d3ccfb95865139f98b184db0e175b.tar.bz2 busybox-w32-e39da802dd6d3ccfb95865139f98b184db0e175b.zip |
klibc-utils: add ipconfig.c work-in-progress
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | klibc-utils/ipconfig.c.txt | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/klibc-utils/ipconfig.c.txt b/klibc-utils/ipconfig.c.txt new file mode 100644 index 000000000..5dd95c16b --- /dev/null +++ b/klibc-utils/ipconfig.c.txt | |||
@@ -0,0 +1,316 @@ | |||
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 | |||
19 | struct 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 | |||
28 | struct 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 | |||
37 | static int | ||
38 | parse_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 | |||
63 | static uint32_t | ||
64 | parse_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 | |||
72 | static struct dev* | ||
73 | find_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 | |||
84 | static void | ||
85 | set_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) | ||
107 | static void | ||
108 | add_all_devices(llist_t **iface_list, struct dev *template); | ||
109 | static struct dev* | ||
110 | add_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 | |||
182 | static void | ||
183 | add_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) | ||
252 | int ipconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
253 | int 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') | ||