netcfg-common.c 48.2 KB
Newer Older
Frans Pop's avatar
Frans Pop committed
1 2
/*
   netcfg-common.c - Shared functions used to configure the network for
3 4 5
   the debian-installer.

   Copyright (C) 2000-2002  David Kimdon <dwhedon@debian.org>
6
                            and others (see debian/copyright)
Frans Pop's avatar
Frans Pop committed
7

8 9 10 11
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
Frans Pop's avatar
Frans Pop committed
12

13 14 15 16
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
Frans Pop's avatar
Frans Pop committed
17

18 19
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
20
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 22 23

*/

24
#include "netcfg.h"
25
#if defined(WIRELESS)
26
#include <iwlib.h>
27
#endif
28
#include <net/if_arp.h>
29
#include <net/ethernet.h>
30
#include <net/if.h>
31
#include <errno.h>
32
#include <assert.h>
33 34 35 36 37 38 39 40 41
#include <ctype.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
42
#include <fcntl.h>
43 44 45
#include <cdebconf/debconfclient.h>
#include <debian-installer.h>
#include <time.h>
46
#include <netdb.h>
47

48 49
#include <ifaddrs.h>

50
#ifdef __linux__
51
#include <netpacket/packet.h>
52 53 54
#define SYSCLASSNET "/sys/class/net/"
#endif /* __linux__ */

55
#ifdef __FreeBSD_kernel__
56
#include <net/if_dl.h>
57 58 59 60 61
#define LO_IF	"lo0"
#else
#define LO_IF	"lo"
#endif

62
/* network config */
Matt Palmer's avatar
Matt Palmer committed
63 64
char hostname[MAXHOSTNAMELEN + 1];
char domain[MAXHOSTNAMELEN + 1];
65
int have_domain = 0;
66

67 68
/* File descriptors for ioctls and such */
int skfd = 0;
69
#ifdef WIRELESS
70
int wfd = 0;
71
#endif
72

73 74 75
/* Count the number of contiguous 1 bits in a 32-bit integer, starting from
 * the MSB. */
static unsigned int count_bits(uint32_t num)
76
{
77
    int count = 0;
Frans Pop's avatar
Frans Pop committed
78

79 80 81
    while (num & 0x80000000) {
        count++;
        num <<= 1;
82
    }
Frans Pop's avatar
Frans Pop committed
83

84 85
    return count;
}
86

87 88 89 90 91 92 93
/* convert a netmask string (eg 255.255.255.0 or ffff:ff::) in +src+ into
 * the length (24) in +dst+.  Return 0 if some sort of failure, or 1 on
 * success.
 */
int inet_ptom (int af, const char *src, unsigned int *dst)
{
    union inX_addr addr;
Frans Pop's avatar
Frans Pop committed
94

95 96 97
    if (!empty_str(src)) {
        if (inet_pton (af, src, &addr) < 0) {
            *dst = 0;
98
            return 0;
99
        }
100
    }
Frans Pop's avatar
Frans Pop committed
101

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
    if (af == AF_INET) {
        *dst = count_bits(ntohl(addr.in4.s_addr));
        return 1;
    } else if (af == AF_INET6) {
        int i, count;
        for (i = 0, *dst = 0; i < 4; i++) {
            count = count_bits(htonl(addr.in6.s6_addr32[i]));
            *dst += count;
            if (count != 32) break;  /* Don't go any further if the mask has finished */
        }
        return 1;
    } else {
        *dst = 0;
        return 0;
    }
117 118
}

119 120
/* convert a length (24) in +src+ into the string netmask (255.255.255.0) in
 * +dst+.  The length of +dst+ is given in +len+, to ensure we don't
121
 * overrun the buffer +dst+.  +dst+ should always be at least NETCFG_ADDRSTRLEN
122 123 124 125
 * bytes long.
 *
 * Returns the address of +dst+ on success, and NULL on failure.
 */
126
const char *inet_mtop (int af, unsigned int src, char *dst, socklen_t len)
127
{
128
    struct in_addr addr;
129 130 131 132 133 134 135 136 137
    
    inet_mton(AF_INET, src, &addr);
    
    return inet_ntop (af, &addr, dst, len);
}

/* convert a mask length (eg 24) in +src+ into the struct in_addr it corresponds
 * to.
 */
138
void inet_mton (int af, unsigned int src, void *dst)
139
{
140
    in_addr_t mask = 0;
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
    struct in_addr *addr;
    struct in6_addr *addr6;
    
    if (af == AF_INET) {
        addr = (struct in_addr *)dst;
        for(; src; src--)
            mask |= 1 << (32 - src);

        addr->s_addr = htonl(mask);
    } else if (af == AF_INET6) {
        unsigned int byte = 0;
        addr6 = (struct in6_addr *)dst;
        /* Clear out the address */
        memset(addr6->s6_addr, 0, 16);
        
        while (src > 7) {
            addr6->s6_addr[byte++] = 0xff;
            src -= 8;
        }
        for (; src; src--)
            addr6->s6_addr[byte] |= 1 << (8 - src);
    }
163 164
}

165 166
void open_sockets (void)
{
167
#ifdef WIRELESS
168
    wfd = iw_sockets_open();
169
#endif
170
    skfd = socket (AF_INET, SOCK_DGRAM, 0);
171 172
}

173 174
#ifdef __linux__

175 176 177
/* Returns non-zero if this interface has an enabled kill switch, otherwise
 * zero.
 */
178
int check_kill_switch(const char *if_name)
179 180 181 182 183 184 185 186
{
    char *temp, *linkbuf;
    const char *killname;
    char killstate;
    size_t len;
    int linklen, killlen;
    int fd = -1;
    int ret = 0;
Frans Pop's avatar
Frans Pop committed
187

188
    /* longest string we need */
189
    len = strlen(SYSCLASSNET) + strlen(if_name) + strlen("/device/rf_kill") + 1;
Frans Pop's avatar
Frans Pop committed
190

191
    temp = malloc(len);
192
    snprintf(temp, len, SYSCLASSNET "%s/driver", if_name);
193 194 195 196
    linkbuf = malloc(1024); /* probably OK ... I hate readlink() */
    linklen = readlink(temp, linkbuf, 1024);
    if (linklen < 0)
        goto out;
Frans Pop's avatar
Frans Pop committed
197

198 199 200 201 202 203
    if (strncmp(linkbuf + linklen - 8, "/ipw2100", 8) == 0)
        killname = "rf_kill";
    else if (strncmp(linkbuf + linklen - 8, "/ipw2200", 8) == 0)
        killname = "rf_kill";
    else
        goto out;
Frans Pop's avatar
Frans Pop committed
204

205
    snprintf(temp, len, SYSCLASSNET "%s/device/%s", if_name, killname);
206 207 208 209 210 211 212 213 214 215 216 217
    di_info("Checking RF kill switch: %s", temp);
    fd = open(temp, O_RDONLY);
    if (fd == -1)
        goto out;
    killlen = read(fd, &killstate, 1);
    if (killlen < 0) {
        di_error("Failed to read RF kill state: %s", strerror(errno));
        goto out;
    } else if (killlen == 0) {
        di_warning("RF kill state file empty");
        goto out;
    }
Frans Pop's avatar
Frans Pop committed
218

219 220 221 222
    if (killstate == '2') {
        di_info("RF kill switch enabled");
        ret = 1;
    }
Frans Pop's avatar
Frans Pop committed
223

224
 out:
225 226
    free(temp);
    free(linkbuf);
227

228 229
    if (fd != -1)
        close(fd);
230

231 232 233
    return ret;
}

234
#else /* !__linux__ */
235
int check_kill_switch(const char *if_name)
236
{
237
    (void)if_name;
238 239 240 241
    return 0;
}
#endif /* __linux__ */

242
#if defined(WIRELESS)
243 244 245 246
int is_raw_80211(const char *iface)
{
    struct ifreq ifr;
    struct sockaddr sa;
Frans Pop's avatar
Frans Pop committed
247

248
    strncpy(ifr.ifr_name, iface, IFNAMSIZ);
Frans Pop's avatar
Frans Pop committed
249

250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
    if (skfd && ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) {
        di_warning("Unable to retrieve interface type.");
        return 0;
    }

    sa = * (struct sockaddr *) &ifr.ifr_hwaddr;
    switch (sa.sa_family) {
    case ARPHRD_IEEE80211:
    case ARPHRD_IEEE80211_PRISM:
    case ARPHRD_IEEE80211_RADIOTAP:
        return 1;

    default:
        return 0;
    }
}
266
#endif
267

268 269 270 271
#if defined(__s390__)
// Layer 3 qeth on s390(x) cannot do arping to test gateway reachability.
int is_layer3_qeth(const char *iface)
{
272
    const int bufsize = 1024;
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
    int retval = 0;
    char* path;
    char* buf;
    size_t len;
    ssize_t slen;
    char* driver;
    int fd;

    // This is sufficient for both /driver and /layer2.
    len = strlen(SYSCLASSNET) + strlen(iface) + strlen("/device/driver") + 1;

    path = malloc(len);
    snprintf(path, len, SYSCLASSNET "%s/device/driver", iface);

    // lstat() on sysfs symlinks does not provide size information.
288 289
    buf = malloc(bufsize);
    slen = readlink(path, buf, bufsize - 1);
290 291 292 293 294 295

    if (slen < 0) {
        di_error("Symlink %s cannot be resolved: %s", path, strerror(errno));
        goto out;
    }

296
    buf[slen] = '\0';
297 298 299

    driver = strrchr(buf, '/') + 1;
    if (strcmp(driver, "qeth") != 0) {
300
        di_info("no qeth found: %s", driver);
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
        goto out;
    }

    snprintf(path, len, SYSCLASSNET "%s/device/layer2", iface);

    fd = open(path, O_RDONLY);
    if (fd == -1) {
        di_error("%s cannot be opened: %s", path, strerror(errno));
        goto out;
    }

    slen = read(fd, buf, 1);
    if (slen == -1) {
        di_error("Read from %s failed: %s", path, strerror(errno));
        close(fd);
        goto out;
    }

    if (buf[0] == '0') {
        // driver == 'qeth' && layer2 == 0
        retval = 1;
    }

    close(fd);

out:
    free(buf);
    free(path);
    return retval;
}
#else
int is_layer3_qeth(const char *iface __attribute__((unused)))
{
    return 0;
}
#endif

338 339 340 341 342 343 344
int qsort_strcmp(const void *a, const void *b)
{
    const char **ia = (const char **)a;
    const char **ib = (const char **)b;
    return strcmp(*ia, *ib);
}

345 346 347 348 349 350 351 352
#ifdef __GNU__
#include <mach.h>
#include <device/device.h>
#include <hurd.h>
/* On Hurd, the IP stack (pfinet) does not know the list of network interfaces
 * before we configure them, so we cannot use getifaddrs(). Instead we try
 * possible names for network interfaces and check whether they exists by
 * attempting to open the kernel device. */
353
int get_all_ifs (int all __attribute__ ((unused)), char*** ptr)
354 355 356
{
    static const char *const fmt[] = { "eth%d", "wl%d", NULL };

357
    mach_port_t device_master, file_master;
358 359 360 361 362
    device_t device;
    int err;
    char **list;
    int num, i, j;
    char name[3 + 3 * sizeof (int) + 1];
363
    char devname[5 + sizeof(name)];
364 365 366

    err = get_privileged_ports (0, &device_master);
    if (err)
367
        return 0;
368 369 370 371

    num = 0;
    list = malloc(sizeof *list);
    for (i = 0; fmt[i]; i++)
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
        for (j = 0;; j++) {
            char *thename;
            sprintf (name, fmt[i], j);
            sprintf (devname, "/dev/%s", name);
            err = device_open (device_master, D_READ, name, &device);
            if (err == 0)
                thename = name;
            else
                {
                    file_master = file_name_lookup (devname, O_READ | O_WRITE, 0);
                    if (file_master == MACH_PORT_NULL)
                        break;

                    err = device_open (file_master, D_READ, name, &device);
                    mach_port_deallocate (mach_task_self (), file_master);
                    if (err != 0)
                        break;
                    thename = devname;
                }

            device_close (device);
            mach_port_deallocate (mach_task_self (), device);

            list = realloc (list, (num + 2) * sizeof *list);
            list[num++] = strdup(thename);
        }
398 399 400 401 402 403 404
    list[num] = NULL;

    mach_port_deallocate (mach_task_self (), device_master);
    *ptr = list;
    return num;
}
#else
405
int get_all_ifs (int all, char*** ptr)
406
{
407 408
    struct ifaddrs *ifap, *ifa;
    char ibuf[512];
409 410
    char** list = NULL;
    size_t len = 0;
Frans Pop's avatar
Frans Pop committed
411

412
    if (getifaddrs(&ifap) == -1)
413
        return 0;
414

415 416
    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
        strncpy(ibuf, ifa->ifa_name, sizeof(ibuf));
417
        if (ifa->ifa_flags & IFF_LOOPBACK)   /* ignore loopback devices */
418
            continue;
419
#if defined(__linux__)
420 421
        if (!strncmp(ibuf, "sit", 3))        /* ignore tunnel devices */
            continue;
422
#endif
423 424 425
#if defined(__FreeBSD_kernel__)
        if (!strncmp(ibuf, "pfsync", 6))     /* ignore pfsync devices */
            continue;
Robert Millan's avatar
Robert Millan committed
426 427
        if (!strncmp(ibuf, "pflog", 5))      /* ignore pflog devices */
            continue;
428 429 430
        if (!strncmp(ibuf, "usbus", 5))      /* ignore usbus devices */
            continue;
#endif
431
#if defined(WIRELESS)
432 433
        if (is_raw_80211(ibuf))
            continue;
434
#endif
435
        if (all || ifa->ifa_flags & IFF_UP) {
436 437 438 439 440 441 442 443 444
            int found = 0;
            size_t i;

            for (i = 0 ; i < len ; i++) {
                if (!strcmp(ibuf, list[i])) {
                    found = 1;
                }
            }
            if (!found) {
445
                list = realloc(list, sizeof(char*) * (len + 2));
446 447 448
                list[len] = strdup(ibuf);
                len++;
            }
449 450
        }
    }
Frans Pop's avatar
Frans Pop committed
451

452
    /* OK, now sort the list and terminate it if necessary */
453
    if (list != NULL) {
454
        qsort(list, len, sizeof(char *), qsort_strcmp);
455 456
        list[len] = NULL;
    }
457
    freeifaddrs(ifap);
Frans Pop's avatar
Frans Pop committed
458

459
    *ptr = list;
Frans Pop's avatar
Frans Pop committed
460

461
    return len;
462
}
463
#endif
464

465
#ifdef __linux__
466
short find_in_stab(const char *if_name)
467
{
468 469
    FILE *dn = NULL;
    char buf[128];
470
    size_t len = strlen(if_name);
Frans Pop's avatar
Frans Pop committed
471

472 473
    if (access(STAB, F_OK) == -1)
        return 0;
Frans Pop's avatar
Frans Pop committed
474

475 476
    if (!(dn = popen("grep -v '^Socket' " STAB " | cut -f5", "r")))
        return 0;
Frans Pop's avatar
Frans Pop committed
477

478
    while (fgets (buf, 128, dn) != NULL) {
479
        if (!strncmp(buf, if_name, len)) {
480 481 482
            pclose(dn);
            return 1;
        }
483
    }
484 485
    pclose(dn);
    return 0;
486
}
487 488
#else /* !__linux__ */
/* Stub function for platforms not supporting /var/run/stab. */
489
short find_in_stab(const char *if_name)
490
{
491
    (void)if_name;
492 493 494
    return 0;
}
#endif /* __linux__ */
495

496 497
char *find_in_devnames(const char* iface)
{
498 499 500
    FILE* dn = NULL;
    char buf[512], *result = NULL;
    size_t len = strlen(iface);
Frans Pop's avatar
Frans Pop committed
501

502 503
    if (!(dn = fopen(DEVNAMES, "r")))
        return NULL;
Frans Pop's avatar
Frans Pop committed
504

505 506
    while (fgets(buf, 512, dn) != NULL) {
        char *ptr = strchr(buf, ':'), *desc = ptr + 1;
Frans Pop's avatar
Frans Pop committed
507

508 509 510 511 512 513 514 515
        if (!ptr) {
            result = NULL; /* corrupt */
            break;
        }
        else if (!strncmp(buf, iface, len)) {
            result = strdup(desc);
            break;
        }
516
    }
Frans Pop's avatar
Frans Pop committed
517

518
    fclose(dn);
Frans Pop's avatar
Frans Pop committed
519

520 521
    if (result) {
        len = strlen(result);
Frans Pop's avatar
Frans Pop committed
522

523 524
        if (result[len - 1] == '\n')
            result[len - 1] = '\0';
525
    }
Frans Pop's avatar
Frans Pop committed
526

527
    return result;
528
}
529

530
char *get_ifdsc(struct debconfclient *client, const char *if_name)
531
{
532
    char template[256], *ptr = NULL;
Frans Pop's avatar
Frans Pop committed
533

534
    if ((ptr = find_in_devnames(if_name)) != NULL) {
535
        debconf_metaget(client, "netcfg/internal-wireless", "description");
Frans Pop's avatar
Frans Pop committed
536

537
        if (is_wireless_iface(if_name)) {
538 539
            size_t len = strlen(ptr) + strlen(client->value) + 4;
            ptr = realloc(ptr, len);
Frans Pop's avatar
Frans Pop committed
540

541 542 543
            di_snprintfcat(ptr, len, " (%s)", client->value);
        }
        return ptr; /* already strdup'd */
544
    }
Frans Pop's avatar
Frans Pop committed
545

546 547
    if (strlen(if_name) < 100) {
        if (!is_wireless_iface(if_name)) {
548
            /* strip away the number from the interface (eth0 -> eth) */
549
            char *ifp = strdup(if_name), *ptr = ifp;
550 551 552
            while ((*ptr < '0' || *ptr > '9') && *ptr != '\0')
                ptr++;
            *ptr = '\0';
Frans Pop's avatar
Frans Pop committed
553

554 555
            sprintf(template, "netcfg/internal-%s", ifp);
            free(ifp);
Frans Pop's avatar
Frans Pop committed
556

557 558
            if (debconf_metaget(client, template, "description") ==
                    CMD_SUCCESS && client->value != NULL) {
559 560 561 562 563 564 565
                return strdup(client->value);
            }
        } else {
            strcpy(template, "netcfg/internal-wifi");
            debconf_metaget(client, template, "description");
            return strdup(client->value);
        }
566 567 568 569 570 571 572 573
    }
    debconf_metaget(client, "netcfg/internal-unknown-iface", "description");
    if (client->value != NULL)
        return strdup(client->value);
    else
        return strdup("Unknown interface");
}

574
int iface_is_hotpluggable(const char *if_name)
575 576 577
{
    FILE* f = NULL;
    char buf[256];
578
    size_t len = strlen(if_name);
Frans Pop's avatar
Frans Pop committed
579

580 581 582
    if (!(f = fopen(DEVHOTPLUG, "r"))) {
        di_info("No hotpluggable devices are present in the system.");
        return 0;
583
    }
Frans Pop's avatar
Frans Pop committed
584

585
    while (fgets(buf, 256, f) != NULL) {
586 587
        if (!strncmp(buf, if_name, len)) {
            di_info("Detected %s as a hotpluggable device", if_name);
588 589 590
            fclose(f);
            return 1;
        }
591
    }
Frans Pop's avatar
Frans Pop committed
592

593
    fclose(f);
Frans Pop's avatar
Frans Pop committed
594

595
    di_info("Hotpluggable devices available, but %s is not one of them", if_name);
596
    return 0;
597
}
598 599 600 601 602 603 604 605 606 607 608 609 610

FILE *file_open(char *path, const char *opentype)
{
    FILE *fp;
    if ((fp = fopen(path, opentype)))
        return fp;
    else {
        fprintf(stderr, "%s\n", path);
        perror("fopen");
        return NULL;
    }
}

611
static char *get_bootif(void)
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
{
#ifdef __linux__
#define PROC_CMDLINE "/proc/cmdline"

    FILE *cmdline_file;
    char *cmdline = NULL;
    size_t dummy;
    const char *s;
    char *bootif = NULL;

    /* Look for BOOTIF= entry in kernel command line. */

    cmdline_file = file_open(PROC_CMDLINE, "r");
    if (!cmdline_file) {
        di_error("Failed to open " PROC_CMDLINE ": %s", strerror(errno));
        return NULL;
    }
    if (getline(&cmdline, &dummy, cmdline_file) < 0) {
        di_error("Failed to read line from " PROC_CMDLINE ": %s",
                 strerror(errno));
        fclose(cmdline_file);
        return NULL;
    }

    s = cmdline;
    while ((s = strstr(s, "BOOTIF=")) != NULL) {
        if (s == cmdline || s[-1] == ' ') {
            size_t bootif_len;
            char *subst;

            s += sizeof("BOOTIF=") - 1;
643
            bootif_len = strcspn(s, " \n");
644
            if (bootif_len != (ETH_ALEN * 3 - 1) + 3)
645 646 647 648 649 650 651 652 653 654 655 656 657
                continue;
            bootif = strndup(s + 3, bootif_len - 3); /* skip hardware type */
            for (subst = bootif; *subst; subst++)
                if (*subst == '-')
                    *subst = ':';
            break;
        }
        s++;
    }

    free(cmdline);
    fclose(cmdline_file);

658 659
    if (!bootif)
        di_info("Could not find valid BOOTIF= entry in " PROC_CMDLINE);
660

661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
    return bootif;

#undef PROC_CMDLINE
#else /* !__linux__ */
    return NULL;
#endif /* __linux__ */
}

static unsigned char *parse_bootif(const char *bootif, int quiet)
{
    int i;
    const char *s;
    unsigned char *bootif_addr = malloc(ETH_ALEN);

    /* Parse supplied address. */
    for (i = 0, s = bootif; i < ETH_ALEN && s; i++) {
        unsigned long bootif_byte;

        errno = 0;
        bootif_byte = strtol(s, (char **) &s, 16);
        if (errno || bootif_byte >= 256) {
            if (!quiet)
                di_error("couldn't parse link-layer address '%s'", bootif);
            free(bootif_addr);
            return NULL;
        }
        bootif_addr[i] = (unsigned char) bootif_byte;
        if (i < ETH_ALEN - 1 && *s++ != ':') {
            if (!quiet)
                di_error("couldn't parse link-layer address '%s'", bootif);
            free(bootif_addr);
            return NULL;
693
        }
694 695 696 697
    }

    return bootif_addr;
}
698

699 700 701
static char *find_bootif_iface(const char *bootif,
                               const unsigned char *bootif_addr)
{
702
#ifdef __GNU__
703
    /* TODO: Use device_get_status(NET_ADDRESS), see pfinet/ethernet.c */
704 705
    (void)bootif;
    (void)bootif_addr;
706 707
    return NULL;
#else
708 709 710 711 712 713 714 715 716
    struct ifaddrs *ifap, *ifa;
    char *ret = NULL;

    /* TODO: this won't work on the Hurd as getifaddrs doesn't return
     * unconfigured interfaces.  See comment to get_all_ifs.
     */
    if (getifaddrs(&ifap) < 0) {
        di_error("getifaddrs failed: %s", strerror(errno));
        return NULL;
717 718
    }

719 720 721 722 723 724
    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
#if defined(__FreeBSD_kernel__)
        struct sockaddr_dl *sdl;
#else
        struct sockaddr_ll *sll;
#endif
725

726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
        if (ifa->ifa_flags & IFF_LOOPBACK)
            continue;
#if defined(__linux__)
        if (!strncmp(ifa->ifa_name, "sit", 3))  /* ignore tunnel devices */
            continue;
#endif
#if defined(WIRELESS)
        if (is_raw_80211(ifa->ifa_name))
            continue;
#endif
#if defined(__FreeBSD_kernel__)
        if (ifa->ifa_addr->sa_family != AF_LINK)
            continue;
        sdl = (struct sockaddr_dl *) ifa->ifa_addr;
        if (!sdl)                               /* no link-layer address */
            continue;
        if (sdl->sdl_alen != ETH_ALEN)          /* not Ethernet */
            continue;
        if (memcmp(bootif_addr, LLADDR(sdl), ETH_ALEN) != 0)
            continue;
#else
        if (ifa->ifa_addr->sa_family != AF_PACKET)
            continue;
        sll = (struct sockaddr_ll *) ifa->ifa_addr;
        if (!sll)                               /* no link-layer address */
            continue;
        if ((sll->sll_hatype != ARPHRD_ETHER &&
             sll->sll_hatype != ARPHRD_IEEE802) ||
            sll->sll_halen != ETH_ALEN)         /* not Ethernet */
            continue;
        if (memcmp(bootif_addr, sll->sll_addr, ETH_ALEN) != 0)
            continue;
#endif
759

760 761 762 763 764 765 766 767 768 769 770 771
        di_info("Found interface %s with link-layer address %s",
                ifa->ifa_name, bootif);
        ret = strdup(ifa->ifa_name);
        break;
    }

    freeifaddrs(ifap);

    if (!ret)
        di_error("Could not find any interface with address %s", bootif);

    return ret;
772
#endif
773 774
}

775 776
void netcfg_die(struct debconfclient *client)
{
777
    debconf_progress_stop(client);
778 779 780 781 782 783 784 785
    debconf_capb(client);
    debconf_input(client, "high", "netcfg/error");
    debconf_go(client);
    exit(1);
}

/**
 * @brief Ask which interface to configure
786 787 788 789
 * @param client    - client
 * @param interface - set the +name+ field to the answer
 * @param numif     - number of interfaces found.
 * @param defif     - default interface from link detection.
790 791 792
 */

int netcfg_get_interface(struct debconfclient *client, char **interface,
793
                         int *numif, const char *defif)
794
{
795
    char *inter = NULL, **ifs;
796
    size_t len;
Joey Hess's avatar
Joey Hess committed
797
    int ret, i, asked;
798
    int num_interfaces = 0;
799 800
    unsigned char *bootif_addr;
    char *bootif_iface = NULL;
801
    char *ptr = NULL;
802
    char *ifdsc = NULL;
Joey Hess's avatar
Joey Hess committed
803
    char *old_selection = NULL;
Frans Pop's avatar
Frans Pop committed
804

805 806 807 808
    if (*interface) {
        free(*interface);
        *interface = NULL;
    }
Frans Pop's avatar
Frans Pop committed
809

810
    if (!(ptr = malloc(128)))
811
        goto error;
Frans Pop's avatar
Frans Pop committed
812

813 814
    len = 128;
    *ptr = '\0';
Frans Pop's avatar
Frans Pop committed
815

816
    num_interfaces = get_all_ifs(1, &ifs);
Frans Pop's avatar
Frans Pop committed
817

818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
    /* Remember old interface selection, in case it's preseeded. */
    debconf_get(client, "netcfg/choose_interface");
    old_selection = strdup(client->value);

    /* If netcfg/choose_interface is preseeded to a link-layer address in
     * the form aa:bb:cc:dd:ee:ff, or if BOOTIF is set and matches an
     * interface, override any provided default from link detection. */
    bootif_addr = parse_bootif(old_selection, 1);
    if (bootif_addr) {
        bootif_iface = find_bootif_iface(old_selection, bootif_addr);
        if (bootif_iface) {
            free(old_selection);
            old_selection = strdup(bootif_iface);
        }
        free(bootif_addr);
    } else {
        char *bootif = get_bootif();
        if (bootif) {
            bootif_addr = parse_bootif(bootif, 0);
            if (bootif_addr) {
                bootif_iface = find_bootif_iface(bootif, bootif_addr);
                free(bootif_addr);
            }
            free(bootif);
        }
    }
    if (bootif_iface) {
        /* Did we actually get back an interface we know about? */
        for (i = 0; i < num_interfaces; i++) {
            if (strcmp(ifs[i], bootif_iface) == 0) {
                defif = ifs[i];
                break;
            }
        }
        free (bootif_iface);
    }
854

Joey Hess's avatar
Joey Hess committed
855 856
    /* If no default was provided, use the first in the list of interfaces. */
    if (! defif && num_interfaces > 0) {
857
        defif = ifs[0];
Joey Hess's avatar
Joey Hess committed
858
    }
Frans Pop's avatar
Frans Pop committed
859

860 861 862
    for (i = 0; i < num_interfaces; i++) {
        size_t newchars;
        char *temp = NULL;
Frans Pop's avatar
Frans Pop committed
863

864
        inter = ifs[i];
Frans Pop's avatar
Frans Pop committed
865

866 867
        interface_down(inter);
        ifdsc = get_ifdsc(client, inter);
868
        newchars = strlen(inter) + strlen(ifdsc) + 5; /* ": , " + NUL */
869 870 871 872 873
        if (len < (strlen(ptr) + newchars)) {
            if (!(ptr = realloc(ptr, len + newchars + 128)))
                goto error;
            len += newchars + 128;
        }
Frans Pop's avatar
Frans Pop committed
874

875
        temp = malloc(newchars);
Frans Pop's avatar
Frans Pop committed
876

Joshua Kwan's avatar
Joshua Kwan committed
877
        snprintf(temp, newchars, "%s: %s", inter, ifdsc);
Frans Pop's avatar
Frans Pop committed
878

879 880 881
        if (num_interfaces > 1 &&
            ((strcmp(defif, inter) == 0) || (strcmp(defif, temp) == 0)))
            debconf_set(client, "netcfg/choose_interface", temp);
Frans Pop's avatar
Frans Pop committed
882

883
        di_snprintfcat(ptr, len, "%s, ", temp);
Frans Pop's avatar
Frans Pop committed
884

885
        free(temp);
886 887
        free(ifdsc);
    }
Frans Pop's avatar
Frans Pop committed
888

889
    if (num_interfaces == 0) {
890
        debconf_input(client, "high", "netcfg/no_interfaces");
Colin Watson's avatar
Colin Watson committed
891
        ret = debconf_go(client);
892
        free(ptr);
893
        free(old_selection);
894
        *numif = 0;
895

Colin Watson's avatar
Colin Watson committed
896
        return ret;
897
    }
898
    else if (num_interfaces == 1) {
899 900
        inter = ptr;
        *numif = 1;
901
        free(old_selection);
902
    }
903
    else if (num_interfaces > 1) {
904 905 906
        *numif = num_interfaces;
        /* remove the trailing ", ", which confuses cdebconf */
        ptr[strlen(ptr) - 2] = '\0';
Frans Pop's avatar
Frans Pop committed
907

908 909
        debconf_subst(client, "netcfg/choose_interface", "ifchoices", ptr);
        free(ptr);
Frans Pop's avatar
Frans Pop committed
910

911
        asked = (debconf_input(client, "critical", "netcfg/choose_interface") == CMD_SUCCESS);
912
        ret = debconf_go(client);
Frans Pop's avatar
Frans Pop committed
913

Joey Hess's avatar
Joey Hess committed
914
        /* If the question is not asked, honor preseeded interface name.
915 916
         * However, if it was preseeded to "auto", or there was no old value,
         * leave it set to defif. */
Joey Hess's avatar
Joey Hess committed
917
        if (!asked && strlen(old_selection) && strcmp(old_selection, "auto") != 0) {
918 919
            debconf_set(client, "netcfg/choose_interface", old_selection);
        }
Frans Pop's avatar
Frans Pop committed
920

921
        free(old_selection);
Frans Pop's avatar
Frans Pop committed
922

923 924
        if (ret)
            return ret;
Frans Pop's avatar
Frans Pop committed
925

926 927
        debconf_get(client, "netcfg/choose_interface");
        inter = client->value;
Frans Pop's avatar
Frans Pop committed
928

929 930
        if (!inter)
            netcfg_die(client);
931
    }
Frans Pop's avatar
Frans Pop committed
932

933 934
    /* grab just the interface name, not the description too */
    *interface = inter;
935 936
    /* Note that the question may be preseeded to just the interface name,
     * with no colon after it. Allow for this case. */
937
    ptr = strchr(inter, ':');
938
    if (ptr != NULL) {
939
        *ptr = '\0';
940
    }
941
    *interface = strdup(*interface);
Frans Pop's avatar
Frans Pop committed
942

943 944
    /* Free allocated memory */
    while (ifs && *ifs)
945
        free(*ifs++);
Frans Pop's avatar
Frans Pop committed
946

947 948 949 950 951
    return 0;

 error:
    if (ptr)
        free(ptr);
Frans Pop's avatar
Frans Pop committed
952

953
    netcfg_die(client);
954
    return RETURN_TO_MAIN; /* unreachable */
955 956
}

Joshua Kwan's avatar
Joshua Kwan committed
957
/*
958 959 960
 * Verify that the hostname conforms to RFC 1123 s2.1,
 * and RFC 1034 s3.5.
 * @return 1 on success, 0 on failure.
Joshua Kwan's avatar
Joshua Kwan committed
961
 */
962
short valid_hostname (const char *hname)
Joshua Kwan's avatar
Joshua Kwan committed
963
{
964
    static const char *valid_chars =
965
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
966 967
    size_t len;
    assert(hname != NULL);
Frans Pop's avatar
Frans Pop committed
968

969
    len = strlen(hname);
Frans Pop's avatar
Frans Pop committed
970

971
    if ((len < 1) ||
972
        (len > MAXHOSTNAMELEN) ||
973 974 975
        (strspn(hname, valid_chars) != len) ||
        (hname[len - 1] == '-') ||
        (hname[0] == '-')) {
976
        return 0;
977 978
    }
    else
979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
        return 1;
}

/*
 * Verify that the domain name (or FQDN) conforms to RFC 1123 s2.1, and
 * RFC1034 s3.5.
 * @return 1 on success, 0 on failure.
 */
short valid_domain (const char *dname)
{
    static const char *valid_chars =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.";
    size_t len;
    assert(dname != NULL);

    len = strlen(dname);

    if ((len < 1) ||
        (len > MAXHOSTNAMELEN) ||
        (strspn(dname, valid_chars) != len) ||
        (dname[len - 1] == '-') ||
        (dname[0] == '-') ||
        (dname[len - 1] == '.') ||
        (dname[0] == '.') ||
        strstr(dname, "..")) {
1004
        return 0;
1005 1006 1007
    }
    else
        return 1;
Joshua Kwan's avatar
Joshua Kwan committed
1008 1009
}

1010
/*
1011 1012 1013
 * Write the hostname to the given string (must be capable of storing at
 * least MAXHOSTNAMELEN bytes.
 *
1014 1015
 * @return 0 on success, 30 on BACKUP being selected.
 */
1016
int netcfg_get_hostname(struct debconfclient *client, char *template, char *hostname, short accept_domain)
1017
{
1018
    char *s, buf[1024];
Frans Pop's avatar
Frans Pop committed
1019

1020
    for(;;) {
1021
        if (accept_domain)
1022
            have_domain = 0;
1023
        debconf_input(client, "high", template);
Frans Pop's avatar
Frans Pop committed
1024

1025 1026
        if (debconf_go(client) == CMD_GOBACK)
            return GO_BACK;
Frans Pop's avatar
Frans Pop committed
1027

1028
        debconf_get(client, template);
Frans Pop's avatar
Frans Pop committed
1029

1030
        strncpy(hostname, client->value, MAXHOSTNAMELEN);
1031

1032 1033
        if (!valid_domain(hostname)) {
            di_info("%s is an invalid domain", hostname);
1034
            debconf_subst(client, "netcfg/invalid_hostname",
1035
                          "hostname", hostname);
1036 1037 1038
            snprintf(buf, sizeof(buf), "%i", MAXHOSTNAMELEN);
            debconf_subst(client, "netcfg/invalid_hostname",
                      "maxhostnamelen", buf);
1039 1040 1041
            debconf_input(client, "high", "netcfg/invalid_hostname");
            debconf_go(client);
            debconf_set(client, template, "debian");
1042
            *hostname = '\0';
1043
            continue;
1044 1045
        }

1046
        if (accept_domain && (s = strchr(hostname, '.'))) {
1047 1048 1049 1050
            di_info("Detected we have an FQDN; splitting and setting domain");
            if (s[1] == '\0') { /* "somehostname." <- . should be ignored */
                *s = '\0';
            } else { /* assume we have a valid domain name given */
Matt Palmer's avatar
Matt Palmer committed
1051
                strncpy(domain, s + 1, MAXHOSTNAMELEN);
1052 1053 1054 1055 1056
                debconf_set(client, "netcfg/get_domain", domain);
                have_domain = 1;
                *s = '\0';
            }
        }
1057 1058 1059
        
        if (!valid_hostname(hostname)) {
            di_info("%s is an invalid hostname", hostname);
1060
            debconf_subst(client, "netcfg/invalid_hostname",
1061
                          "hostname", hostname);
1062 1063 1064 1065 1066 1067
            snprintf(buf, sizeof(buf), "%i", MAXHOSTNAMELEN);
            debconf_subst(client, "netcfg/invalid_hostname",
                      "maxhostnamelen", buf);
            debconf_input(client, "high", "netcfg/invalid_hostname");
            debconf_go(client);
            debconf_set(client, template, "debian");
1068
            *hostname = '\0';
1069 1070
        } else {
            break;
Joshua Kwan's avatar
Joshua Kwan committed
1071 1072
        }
    }
Frans Pop's avatar
Frans Pop committed
1073

1074 1075 1076
    return 0;
}

1077
/* @brief Get the domainname.
1078
 * @return 0 for success, with *domain = domain, GO_BACK for 'goback',
1079
 */
Matt Palmer's avatar
Matt Palmer committed
1080
int netcfg_get_domain(struct debconfclient *client,  char domain[])
1081 1082
{
    int ret;
Frans Pop's avatar
Frans Pop committed
1083

1084 1085
    if (have_domain == 1)
    {
1086 1087
        debconf_get(client, "netcfg/get_domain");
        assert (!empty_str(client->value));
Matt Palmer's avatar
Matt Palmer committed
1088
        strncpy(domain, client->value, MAXHOSTNAMELEN);
1089
        return 0;
1090 1091 1092 1093
    }

    debconf_input (client, "high", "netcfg/get_domain");
    ret = debconf_go(client);
Frans Pop's avatar
Frans Pop committed
1094

1095
    if (ret)
1096
        return ret;
Frans Pop's avatar
Frans Pop committed
1097

1098
    debconf_get (client, "netcfg/get_domain");
Frans Pop's avatar
Frans Pop committed
1099

Matt Palmer's avatar
Matt Palmer committed
1100
    *domain = '\0';
1101 1102 1103 1104
    if (!empty_str(client->value)) {
        const char *start = client->value;
        while (*start == '.')
            ++start; /* trim leading dots */
Matt Palmer's avatar
Matt Palmer committed
1105
        strncpy(domain, start, MAXHOSTNAMELEN);
1106
    }
1107

1108 1109 1110
    return 0;
}

1111
void netcfg_write_loopback (void)
1112
{
1113 1114 1115 1116 1117 1118 1119 1120
    struct netcfg_interface lo;
    
    netcfg_interface_init(&lo);
    lo.name = LO_IF;
    lo.loopback = 1;
    
    netcfg_write_interface(NULL);
    netcfg_write_interface(&lo);
1121 1122
}

1123 1124 1125 1126 1127 1128
/*
 * ipaddress.s_addr may be 0
 * domain may be null
 * interface may be null
 * hostname may _not_ be null
 */
1129
void netcfg_write_common(const char *ipaddress, const char *hostname, const char *domain)
1130 1131
{
    FILE *fp;
1132
    char *domain_nodot = NULL;
Frans Pop's avatar
Frans Pop committed
1133

1134
    if (empty_str(hostname))
1135
        return;
Frans Pop's avatar
Frans Pop committed
1136

1137 1138 1139 1140 1141 1142 1143 1144 1145 1146
    if (domain) {
        char *end;

        /* strip trailing dots */
        domain_nodot = strdup(domain);
        end = domain_nodot + strlen(domain_nodot) - 1;
        while (end >= domain_nodot && *end == '.')
            *end-- = '\0';
    }

Joey Hess's avatar
Joey Hess committed
1147
    /* Currently busybox, hostname is not available. */
1148 1149 1150
    if (sethostname (hostname, strlen(hostname) + 1) < 0) {
        /* ignore errors */
    }
Frans Pop's avatar
Frans Pop committed
1151

Joey Hess's avatar
Joey Hess committed
1152
    if ((fp = file_open(HOSTNAME_FILE, "w"))) {
1153 1154
        fprintf(fp, "%s\n", hostname);
        fclose(fp);
Joey Hess's avatar
Joey Hess committed
1155
    }
Frans Pop's avatar
Frans Pop committed
1156

1157
    if ((fp = file_open(HOSTS_FILE, "w"))) {
1158
        fprintf(fp, "127.0.0.1\tlocalhost");
Frans Pop's avatar
Frans Pop committed
1159

1160
        if (!empty_str(ipaddress)) {
1161
            if (domain_nodot && !empty_str(domain_nodot))
1162
                fprintf(fp, "\n%s\t%s.%s\t%s\n", ipaddress, hostname, domain_nodot, hostname);
1163
            else
1164
                fprintf(fp, "\n%s\t%s\n", ipaddress, hostname);
1165
        } else {
1166
#if defined(__linux__) || defined(__GNU__)
1167
            if (domain_nodot && !empty_str(domain_nodot))
1168
                fprintf(fp, "\n127.0.1.1\t%s.%s\t%s\n", hostname, domain_nodot, hostname);
1169
            else
1170 1171 1172 1173
                fprintf(fp, "\n127.0.1.1\t%s\n", hostname);
#else
            fprintf(fp, "\t%s\n", hostname);
#endif
1174
        }
Frans Pop's avatar
Frans Pop committed
1175

1176
        fprintf(fp, "\n" IPV6_HOSTS);
Frans Pop's avatar
Frans Pop committed
1177

1178 1179
        fclose(fp);
    }
1180 1181

    free(domain_nodot);
1182 1183 1184
}


1185
void deconfigure_network(struct netcfg_interface *iface)
1186
{
1187
    /* deconfiguring network interfaces */
1188
    interface_down(LO_IF);
1189 1190
    if (iface)
        interface_down(iface->name);
1191
}
1192 1193 1194

void loop_setup(void)
{
1195
    static int afpacket_notloaded = 1;
Frans Pop's avatar
Frans Pop committed
1196

1197
    deconfigure_network(NULL);
Frans Pop's avatar
Frans Pop committed
1198

1199
#if defined(__FreeBSD_kernel__)
1200
    (void)afpacket_notloaded;
1201 1202 1203 1204
    /* GNU/kFreeBSD currently uses the ifconfig command */
    di_exec_shell_log("ifconfig "LO_IF" up");
    di_exec_shell_log("ifconfig "LO_IF" 127.0.0.1 netmask 255.0.0.0");
#else
1205 1206
    if (afpacket_notloaded)
        afpacket_notloaded = di_exec_shell("modprobe af_packet"); /* should become 0 */
Frans Pop's avatar
Frans Pop committed
1207

1208
    di_exec_shell_log("ip link set "LO_IF" up");
1209
    di_exec_shell_log("ip -f inet addr flush dev "LO_IF);
1210
    di_exec_shell_log("ip addr add 127.0.0.1/8 dev "LO_IF);
1211
#endif
1212 1213
}

1214 1215 1216 1217 1218 1219 1220 1221
/* Determines the IP address of the interface (either from the static
 * configuration, or by querying the interface directly if using some sort
 * of autoconfiguration), then uses that address to request rDNS lookup
 * using the currently configured nameservers.  We return the name in
 * +hostname+ if one is found, and return 1, otherwise we leave the
 * +hostname+ alone and return 0.
 */
int get_hostname_from_dns (const struct netcfg_interface *interface, char *hostname, const size_t max_hostname_len)
1222
{
1223
    int err = 1;
1224
    
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
    if (!empty_str(interface->ipaddress)) {
        /* Static configuration assumed */
        struct sockaddr_in sin;
        struct sockaddr_in6 sin6;

        di_debug("Getting default hostname from rDNS lookup of static-configured address %s", interface->ipaddress);
        if (interface->address_family == AF_INET) {
            sin.sin_family = AF_INET;
            sin.sin_port = 0;
            inet_pton(AF_INET, interface->ipaddress, &sin.sin_addr);
            err = getnameinfo((struct sockaddr *) &sin, sizeof(sin),
                              hostname, max_hostname_len, NULL, 0, NI_NAMEREQD);
        } else if (interface->address_family == AF_INET6) {
            sin6.sin6_family = AF_INET6;
            sin6.sin6_port = 0;
            inet_pton(AF_INET6, interface->ipaddress, &sin6.sin6_addr);
            err = getnameinfo((struct sockaddr *) &sin6, sizeof(sin6),
                              hostname, max_hostname_len, NULL, 0, NI_NAMEREQD);
        } else {
            di_warning("Unknown address family in interface passed to seed_hostname_from_dns(): %i", interface->address_family);
            return 0;
        }
1247 1248

        if (err) {
1249
            di_debug("getnameinfo() returned %i (%s)", err, err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
1250 1251 1252 1253 1254 1255
        }

        if (err == 0) {
            /* We found a name!  We found a name! */
            di_debug("Hostname found: %s", hostname);
        }
1256 1257 1258 1259 <