udhcpc6: add ELAPSED_TIME option to outgoing packets
[oweals/busybox.git] / klibc-utils / ipconfig.c.txt
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')