busybox/klibc-utils/ipconfig.c.txt
<<
>>
Prefs
   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