ifconfig.c 28.7 KB
Newer Older
Phil Blundell's avatar
Phil Blundell committed
1
/*
2 3 4
 * ifconfig   This file contains an implementation of the command
 *              that either displays or sets the characteristics of
 *              one or more of the system's networking interfaces.
Phil Blundell's avatar
Phil Blundell committed
5
 *
6
 * Version:     $Id: ifconfig.c,v 1.59 2011-01-01 03:22:31 ecki Exp $
Phil Blundell's avatar
Phil Blundell committed
7
 *
8
 * Author:      Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
Phil Blundell's avatar
Phil Blundell committed
9 10
 *              and others.  Copyright 1993 MicroWalt Corporation
 *
11 12 13 14 15
 *              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.
16 17 18 19
 *
 * Patched to support 'add' and 'del' keywords for INET(4) addresses
 * by Mrs. Brisby <mrs.brisby@nimh.org>
 *
20 21
 * {1.34} - 19980630 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
 *                     - gettext instead of catgets for i18n
22 23
 *          10/1998  - Andi Kleen. Use interface list primitives.
 *	    20001008 - Bernd Eckenfels, Patch from RH for setting mtu
24
 *			(default AF was wrong)
25
 *          20010404 - Arnaldo Carvalho de Melo, use setlocale
Phil Blundell's avatar
Phil Blundell committed
26 27
 */

28 29
#define DFLT_AF "inet"

Phil Blundell's avatar
Phil Blundell committed
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
#include "config.h"

#include <features.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>

/* Ugh.  But libc5 doesn't provide POSIX types.  */
#include <asm/types.h>

51

52
#if HAVE_HWSLIP
53 54 55
#include <linux/if_slip.h>
#endif

Phil Blundell's avatar
Phil Blundell committed
56 57
#if HAVE_AFINET6

58
#ifndef _LINUX_IN6_H
Phil Blundell's avatar
Phil Blundell committed
59
/*
60
 *    This is in linux/include/net/ipv6.h.
Phil Blundell's avatar
Phil Blundell committed
61 62 63
 */

struct in6_ifreq {
64 65 66
    struct in6_addr ifr6_addr;
    __u32 ifr6_prefixlen;
    unsigned int ifr6_ifindex;
Phil Blundell's avatar
Phil Blundell committed
67
};
68 69

#endif
70 71

#endif				/* HAVE_AFINET6 */
Phil Blundell's avatar
Phil Blundell committed
72 73

#if HAVE_AFIPX
74 75 76
#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
#include <netipx/ipx.h>
#else
Phil Blundell's avatar
Phil Blundell committed
77 78
#include "ipx.h"
#endif
79
#endif
Phil Blundell's avatar
Phil Blundell committed
80 81 82
#include "net-support.h"
#include "pathnames.h"
#include "version.h"
83
#include "../intl.h"
84 85
#include "interface.h"
#include "sockets.h"
86
#include "util.h"
Phil Blundell's avatar
Phil Blundell committed
87

88
static char *Release = RELEASE;
Phil Blundell's avatar
Phil Blundell committed
89

90 91
int opt_a = 0;			/* show all interfaces          */
int opt_v = 0;			/* debugging output flag        */
Phil Blundell's avatar
Phil Blundell committed
92

93
int addr_family = 0;		/* currently selected AF        */
Phil Blundell's avatar
Phil Blundell committed
94

95
/* for ipv4 add/del modes */
96 97
static int get_nmbc_parent(char *parent, in_addr_t *nm, in_addr_t *bc);
static int set_ifstate(char *parent, in_addr_t ip, in_addr_t nm, in_addr_t bc,
98
		       int flag);
Phil Blundell's avatar
Phil Blundell committed
99

100
static int if_print(char *ifname)
101
{
102 103 104
    int res;

    if (ife_short)
105
	printf(_("Iface      MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg\n"));
106

107
    if (!ifname) {
108
	res = for_all_interfaces(do_if_print, &opt_a);
109
    } else {
110 111
	struct interface *ife;

Andi Kleen's avatar
Andi Kleen committed
112
	ife = lookup_interface(ifname);
113 114 115
	if (!ife) {
		return -1;
	}
116 117
	res = do_if_fetch(ife);
	if (res >= 0)
118
	    ife_print(ife);
Phil Blundell's avatar
Phil Blundell committed
119
    }
120
    return res;
Phil Blundell's avatar
Phil Blundell committed
121 122 123
}

/* Set a certain interface flag. */
124
static int set_flag(char *ifname, short flag)
Phil Blundell's avatar
Phil Blundell committed
125
{
126 127
    struct ifreq ifr;

Phil Blundell's avatar
Phil Blundell committed
128
    safe_strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
129
    if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) {
130
	fprintf(stderr, _("%s: ERROR while getting interface flags: %s\n"),
131
		ifname,	strerror(errno));
132 133
	return (-1);
    }
134
    safe_strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
135 136 137 138 139 140
    ifr.ifr_flags |= flag;
    if (ioctl(skfd, SIOCSIFFLAGS, &ifr) < 0) {
	perror("SIOCSIFFLAGS");
	return -1;
    }
    return (0);
Phil Blundell's avatar
Phil Blundell committed
141 142 143
}

/* Clear a certain interface flag. */
144
static int clr_flag(char *ifname, short flag)
Phil Blundell's avatar
Phil Blundell committed
145
{
146
    struct ifreq ifr;
147 148 149 150 151 152 153 154 155 156 157 158
    int fd;

    if (strchr(ifname, ':')) {
        /* This is a v4 alias interface.  Downing it via a socket for
	   another AF may have bad consequences. */
        fd = get_socket_for_af(AF_INET);
	if (fd < 0) {
	    fprintf(stderr, _("No support for INET on this system.\n"));
	    return -1;
	}
    } else
        fd = skfd;
159

160
    safe_strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
161
    if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
162
	fprintf(stderr, _("%s: ERROR while getting interface flags: %s\n"),
163
		ifname, strerror(errno));
164 165
	return -1;
    }
166
    safe_strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
167
    ifr.ifr_flags &= ~flag;
168
    if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
169 170 171 172
	perror("SIOCSIFFLAGS");
	return -1;
    }
    return (0);
Phil Blundell's avatar
Phil Blundell committed
173 174
}

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
/** test is a specified flag is set */
static int test_flag(char *ifname, short flags)
{
    struct ifreq ifr;
    int fd;

    if (strchr(ifname, ':')) {
        /* This is a v4 alias interface.  Downing it via a socket for
	   another AF may have bad consequences. */
        fd = get_socket_for_af(AF_INET);
	if (fd < 0) {
	    fprintf(stderr, _("No support for INET on this system.\n"));
	    return -1;
	}
    } else
        fd = skfd;

    safe_strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
    if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
194
	fprintf(stderr, _("%s: ERROR while testing interface flags: %s\n"),
195 196 197 198 199 200
		ifname, strerror(errno));
	return -1;
    }
    return (ifr.ifr_flags & flags);
}

201
static void usage(int rc)
Phil Blundell's avatar
Phil Blundell committed
202
{
203 204
    FILE *fp = rc ? stderr : stdout;
    fprintf(fp, _("Usage:\n  ifconfig [-a] [-v] [-s] <interface> [[<AF>] <address>]\n"));
205
#if HAVE_AFINET
206 207 208 209
    fprintf(fp, _("  [add <address>[/<prefixlen>]]\n"));
    fprintf(fp, _("  [del <address>[/<prefixlen>]]\n"));
    fprintf(fp, _("  [[-]broadcast [<address>]]  [[-]pointopoint [<address>]]\n"));
    fprintf(fp, _("  [netmask <address>]  [dstaddr <address>]  [tunnel <address>]\n"));
Phil Blundell's avatar
Phil Blundell committed
210
#endif
211
#ifdef SIOCSKEEPALIVE
212
    fprintf(fp, _("  [outfill <NN>] [keepalive <NN>]\n"));
213
#endif
214 215 216 217
    fprintf(fp, _("  [hw <HW> <address>]  [mtu <NN>]\n"));
    fprintf(fp, _("  [[-]trailers]  [[-]arp]  [[-]allmulti]\n"));
    fprintf(fp, _("  [multicast]  [[-]promisc]\n"));
    fprintf(fp, _("  [mem_start <NN>]  [io_addr <NN>]  [irq <NN>]  [media <type>]\n"));
218
#ifdef HAVE_TXQUEUELEN
219
    fprintf(fp, _("  [txqueuelen <NN>]\n"));
220 221
#endif
#ifdef HAVE_DYNAMIC
222
    fprintf(fp, _("  [[-]dynamic]\n"));
223
#endif
224
    fprintf(fp, _("  [up|down] ...\n\n"));
225

226 227
    fprintf(fp, _("  <HW>=Hardware Type.\n"));
    fprintf(fp, _("  List of possible hardware types:\n"));
228
    print_hwlist(0); /* 1 = ARPable */
229 230
    fprintf(fp, _("  <AF>=Address family. Default: %s\n"), DFLT_AF);
    fprintf(fp, _("  List of possible address families:\n"));
231
    print_aflist(0); /* 1 = routeable */
232
    exit(rc);
Phil Blundell's avatar
Phil Blundell committed
233 234
}

235
static void version(void)
Phil Blundell's avatar
Phil Blundell committed
236
{
237
    printf("%s\n", Release);
238
    exit(E_VERSION);
Phil Blundell's avatar
Phil Blundell committed
239 240
}

241
static int set_netmask(int skfd, struct ifreq *ifr, struct sockaddr *sa)
242
{
243
    int err = 0;
244

245
    memcpy(&ifr->ifr_netmask, sa, sizeof(struct sockaddr));
246 247 248 249 250
    if (ioctl(skfd, SIOCSIFNETMASK, ifr) < 0) {
	fprintf(stderr, "SIOCSIFNETMASK: %s\n",
		strerror(errno));
	err = 1;
    }
251
    return err;
252 253
}

254
int main(int argc, char **argv)
Phil Blundell's avatar
Phil Blundell committed
255
{
256 257 258
    struct sockaddr_storage _sa, _samask;
    struct sockaddr *sa = (struct sockaddr *)&_sa;
    struct sockaddr *samask = (struct sockaddr *)&_samask;
259
    struct sockaddr_in *sin = (struct sockaddr_in *)&_sa;
260
    char host[128];
261
    const struct aftype *ap;
262
    const struct hwtype *hw;
263
    struct ifreq ifr;
264
    int goterr = 0, didnetmask = 0, neednetmask=0;
265 266
    char **spp;
    int fd;
Phil Blundell's avatar
Phil Blundell committed
267
#if HAVE_AFINET6
268
    extern struct aftype inet6_aftype;
269
    struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&_sa;
270 271 272
    struct in6_ifreq ifr6;
    unsigned long prefix_len;
    char *cp;
Phil Blundell's avatar
Phil Blundell committed
273
#endif
274 275 276
#if HAVE_AFINET
    extern struct aftype inet_aftype;
#endif
277

278
#if I18N
279
    setlocale(LC_ALL, "");
280 281
    bindtextdomain("net-tools", "/usr/share/locale");
    textdomain("net-tools");
Phil Blundell's avatar
Phil Blundell committed
282 283
#endif

284 285 286 287 288 289
    /* Find any options. */
    argc--;
    argv++;
    while (argc && *argv[0] == '-') {
	if (!strcmp(*argv, "-a"))
	    opt_a = 1;
Phil Blundell's avatar
Phil Blundell committed
290

291 292
	else if (!strcmp(*argv, "-s"))
	    ife_short = 1;
Phil Blundell's avatar
Phil Blundell committed
293

294 295
	else if (!strcmp(*argv, "-v"))
	    opt_v = 1;
296

297
	else if (!strcmp(*argv, "-V") || !strcmp(*argv, "-version") ||
298 299
	    !strcmp(*argv, "--version"))
	    version();
Phil Blundell's avatar
Phil Blundell committed
300

301
	else if (!strcmp(*argv, "-?") || !strcmp(*argv, "-h") ||
302
	    !strcmp(*argv, "-help") || !strcmp(*argv, "--help"))
303
	    usage(E_USAGE);
304

305
	else {
306
	    fprintf(stderr, _("ifconfig: option `%s' not recognised.\n"),
307 308
		    argv[0]);
	    fprintf(stderr, _("ifconfig: `--help' gives usage information.\n"));
309 310 311
	    exit(1);
	}

312 313
	argv++;
	argc--;
Phil Blundell's avatar
Phil Blundell committed
314 315
    }

316 317 318 319 320 321
    /* Create a channel to the NET kernel. */
    if ((skfd = sockets_open(0)) < 0) {
	perror("socket");
	exit(1);
    }

322 323
    /* Do we have to show the current setup? */
    if (argc == 0) {
324
	int err = if_print((char *) NULL);
325
	(void) close(skfd);
326
	exit(err < 0);
327 328 329
    }
    /* No. Fetch the interface name. */
    spp = argv;
330
    safe_strncpy(ifr.ifr_name, *spp++, IFNAMSIZ);
331
    if (*spp == (char *) NULL) {
332
	int err = if_print(ifr.ifr_name);
333
	(void) close(skfd);
334
	exit(err < 0);
Phil Blundell's avatar
Phil Blundell committed
335
    }
336

337
    /* The next argument is either an address family name, or an option. */
338 339
    if ((ap = get_aftype(*spp)) != NULL)
	spp++; /* it was a AF name */
340
    else
341
	ap = get_aftype(DFLT_AF);
342

343
    if (ap) {
Phil Blundell's avatar
Phil Blundell committed
344
	addr_family = ap->af;
345
	skfd = ap->fd;
346
    }
347

348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
    /* Process the remaining arguments. */
    while (*spp != (char *) NULL) {
	if (!strcmp(*spp, "arp")) {
	    goterr |= clr_flag(ifr.ifr_name, IFF_NOARP);
	    spp++;
	    continue;
	}
	if (!strcmp(*spp, "-arp")) {
	    goterr |= set_flag(ifr.ifr_name, IFF_NOARP);
	    spp++;
	    continue;
	}
#ifdef IFF_PORTSEL
	if (!strcmp(*spp, "media") || !strcmp(*spp, "port")) {
	    if (*++spp == NULL)
363
		usage(E_OPTERR);
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
	    if (!strcasecmp(*spp, "auto")) {
		goterr |= set_flag(ifr.ifr_name, IFF_AUTOMEDIA);
	    } else {
		int i, j, newport;
		char *endp;
		newport = strtol(*spp, &endp, 10);
		if (*endp != 0) {
		    newport = -1;
		    for (i = 0; if_port_text[i][0] && newport == -1; i++) {
			for (j = 0; if_port_text[i][j]; j++) {
			    if (!strcasecmp(*spp, if_port_text[i][j])) {
				newport = i;
				break;
			    }
			}
		    }
		}
		spp++;
		if (newport == -1) {
		    fprintf(stderr, _("Unknown media type.\n"));
		    goterr = 1;
		} else {
		    if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0) {
387
			perror("port: SIOCGIFMAP");
388 389 390 391 392
			goterr = 1;
			continue;
		    }
		    ifr.ifr_map.port = newport;
		    if (ioctl(skfd, SIOCSIFMAP, &ifr) < 0) {
393
			perror("port: SIOCSIFMAP");
394 395 396 397 398 399 400
			goterr = 1;
		    }
		}
	    }
	    continue;
	}
#endif
Phil Blundell's avatar
Phil Blundell committed
401

402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
	if (!strcmp(*spp, "trailers")) {
	    goterr |= clr_flag(ifr.ifr_name, IFF_NOTRAILERS);
	    spp++;
	    continue;
	}
	if (!strcmp(*spp, "-trailers")) {
	    goterr |= set_flag(ifr.ifr_name, IFF_NOTRAILERS);
	    spp++;
	    continue;
	}
	if (!strcmp(*spp, "promisc")) {
	    goterr |= set_flag(ifr.ifr_name, IFF_PROMISC);
	    spp++;
	    continue;
	}
	if (!strcmp(*spp, "-promisc")) {
	    goterr |= clr_flag(ifr.ifr_name, IFF_PROMISC);
419 420
	    if (test_flag(ifr.ifr_name, IFF_PROMISC) > 0)
	    	fprintf(stderr, _("Warning: Interface %s still in promisc mode... maybe other application is running?\n"), ifr.ifr_name);
421 422 423 424 425 426 427 428 429 430
	    spp++;
	    continue;
	}
	if (!strcmp(*spp, "multicast")) {
	    goterr |= set_flag(ifr.ifr_name, IFF_MULTICAST);
	    spp++;
	    continue;
	}
	if (!strcmp(*spp, "-multicast")) {
	    goterr |= clr_flag(ifr.ifr_name, IFF_MULTICAST);
431 432
	    if (test_flag(ifr.ifr_name, IFF_MULTICAST) > 0)
	    	fprintf(stderr, _("Warning: Interface %s still in MULTICAST mode.\n"), ifr.ifr_name);
433 434 435 436 437 438 439 440 441 442
	    spp++;
	    continue;
	}
	if (!strcmp(*spp, "allmulti")) {
	    goterr |= set_flag(ifr.ifr_name, IFF_ALLMULTI);
	    spp++;
	    continue;
	}
	if (!strcmp(*spp, "-allmulti")) {
	    goterr |= clr_flag(ifr.ifr_name, IFF_ALLMULTI);
443
	    if (test_flag(ifr.ifr_name, IFF_ALLMULTI) > 0)
444
	    	fprintf(stderr, _("Warning: Interface %s still in ALLMULTI mode.\n"), ifr.ifr_name);
445 446 447 448 449 450 451 452 453 454 455 456 457
	    spp++;
	    continue;
	}
	if (!strcmp(*spp, "up")) {
	    goterr |= set_flag(ifr.ifr_name, (IFF_UP | IFF_RUNNING));
	    spp++;
	    continue;
	}
	if (!strcmp(*spp, "down")) {
	    goterr |= clr_flag(ifr.ifr_name, IFF_UP);
	    spp++;
	    continue;
	}
458
#ifdef HAVE_DYNAMIC
459 460 461 462
	if (!strcmp(*spp, "dynamic")) {
	    goterr |= set_flag(ifr.ifr_name, IFF_DYNAMIC);
	    spp++;
	    continue;
463 464
	}
	if (!strcmp(*spp, "-dynamic")) {
465 466
	    goterr |= clr_flag(ifr.ifr_name, IFF_DYNAMIC);
	    spp++;
467
	    if (test_flag(ifr.ifr_name, IFF_DYNAMIC) > 0)
468
	    	fprintf(stderr, _("Warning: Interface %s still in DYNAMIC mode.\n"), ifr.ifr_name);
469
	    continue;
470 471 472
	}
#endif

473 474
	if (!strcmp(*spp, "mtu")) {
	    if (*++spp == NULL)
475
		usage(E_OPTERR);
476 477 478 479 480 481 482 483
	    ifr.ifr_mtu = atoi(*spp);
	    if (ioctl(skfd, SIOCSIFMTU, &ifr) < 0) {
		fprintf(stderr, "SIOCSIFMTU: %s\n", strerror(errno));
		goterr = 1;
	    }
	    spp++;
	    continue;
	}
484
#ifdef SIOCSKEEPALIVE
485 486
	if (!strcmp(*spp, "keepalive")) {
	    if (*++spp == NULL)
487
		usage(E_OPTERR);
488
	    ifr.ifr_data = (caddr_t) (uintptr_t) atoi(*spp);
489 490 491 492 493 494 495
	    if (ioctl(skfd, SIOCSKEEPALIVE, &ifr) < 0) {
		fprintf(stderr, "SIOCSKEEPALIVE: %s\n", strerror(errno));
		goterr = 1;
	    }
	    spp++;
	    continue;
	}
496 497 498
#endif

#ifdef SIOCSOUTFILL
499 500
	if (!strcmp(*spp, "outfill")) {
	    if (*++spp == NULL)
501
		usage(E_OPTERR);
502
	    ifr.ifr_data = (caddr_t) (uintptr_t) atoi(*spp);
503 504 505 506 507 508 509
	    if (ioctl(skfd, SIOCSOUTFILL, &ifr) < 0) {
		fprintf(stderr, "SIOCSOUTFILL: %s\n", strerror(errno));
		goterr = 1;
	    }
	    spp++;
	    continue;
	}
510
#endif
511 512 513

	if (!strcmp(*spp, "-broadcast")) {
	    goterr |= clr_flag(ifr.ifr_name, IFF_BROADCAST);
514
	    if (test_flag(ifr.ifr_name, IFF_BROADCAST) > 0)
515
	    	fprintf(stderr, _("Warning: Interface %s still in BROADCAST mode.\n"), ifr.ifr_name);
516 517
	    spp++;
	    continue;
Phil Blundell's avatar
Phil Blundell committed
518
	}
519 520
	if (!strcmp(*spp, "broadcast")) {
	    if (*++spp != NULL) {
521
		safe_strncpy(host, *spp, (sizeof host));
522
		if (ap->input(0, host, &_sa) < 0) {
523 524 525 526
		    if (ap->herror)
		    	ap->herror(host);
		    else
		    	fprintf(stderr, _("ifconfig: Error resolving '%s' for broadcast\n"), host);
527 528 529 530
		    goterr = 1;
		    spp++;
		    continue;
		}
531
		memcpy(&ifr.ifr_broadaddr, sa, sizeof(struct sockaddr));
532 533 534 535 536 537 538 539 540 541 542 543
		if (ioctl(ap->fd, SIOCSIFBRDADDR, &ifr) < 0) {
		    fprintf(stderr, "SIOCSIFBRDADDR: %s\n",
			    strerror(errno));
		    goterr = 1;
		}
		spp++;
	    }
	    goterr |= set_flag(ifr.ifr_name, IFF_BROADCAST);
	    continue;
	}
	if (!strcmp(*spp, "dstaddr")) {
	    if (*++spp == NULL)
544
		usage(E_OPTERR);
545
	    safe_strncpy(host, *spp, (sizeof host));
546
	    if (ap->input(0, host, &_sa) < 0) {
547 548 549 550
		    if (ap->herror)
		    	ap->herror(host);
		    else
		    	fprintf(stderr, _("ifconfig: Error resolving '%s' for dstaddr\n"), host);
551 552 553 554
		goterr = 1;
		spp++;
		continue;
	    }
555
	    memcpy(&ifr.ifr_dstaddr, sa, sizeof(struct sockaddr));
556 557 558 559 560 561 562 563 564 565
	    if (ioctl(ap->fd, SIOCSIFDSTADDR, &ifr) < 0) {
		fprintf(stderr, "SIOCSIFDSTADDR: %s\n",
			strerror(errno));
		goterr = 1;
	    }
	    spp++;
	    continue;
	}
	if (!strcmp(*spp, "netmask")) {
	    if (*++spp == NULL || didnetmask)
566
		usage(E_OPTERR);
567
	    safe_strncpy(host, *spp, (sizeof host));
568
	    if (ap->input(0, host, &_sa) < 0) {
569 570 571 572
		    if (ap->herror)
		    	ap->herror(host);
		    else
		    	fprintf(stderr, _("ifconfig: Error resolving '%s' for netmask\n"), host);
573 574 575 576 577
		goterr = 1;
		spp++;
		continue;
	    }
	    didnetmask++;
578
	    goterr |= set_netmask(ap->fd, &ifr, sa);
579 580
	    spp++;
	    continue;
Phil Blundell's avatar
Phil Blundell committed
581
	}
582
#ifdef HAVE_TXQUEUELEN
583 584
	if (!strcmp(*spp, "txqueuelen")) {
	    if (*++spp == NULL)
585
		usage(E_OPTERR);
586 587 588 589 590 591 592 593
	    ifr.ifr_qlen = strtoul(*spp, NULL, 0);
	    if (ioctl(skfd, SIOCSIFTXQLEN, &ifr) < 0) {
		fprintf(stderr, "SIOCSIFTXQLEN: %s\n", strerror(errno));
		goterr = 1;
	    }
	    spp++;
	    continue;
	}
594
#endif
595

596 597
	if (!strcmp(*spp, "mem_start")) {
	    if (*++spp == NULL)
598
		usage(E_OPTERR);
599
	    if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0) {
600 601
		fprintf(stderr, "mem_start: SIOCGIFMAP: %s\n", strerror(errno));
		spp++;
602 603 604 605 606
		goterr = 1;
		continue;
	    }
	    ifr.ifr_map.mem_start = strtoul(*spp, NULL, 0);
	    if (ioctl(skfd, SIOCSIFMAP, &ifr) < 0) {
607
		fprintf(stderr, "mem_start: SIOCSIFMAP: %s\n", strerror(errno));
608 609 610 611
		goterr = 1;
	    }
	    spp++;
	    continue;
Phil Blundell's avatar
Phil Blundell committed
612
	}
613 614
	if (!strcmp(*spp, "io_addr")) {
	    if (*++spp == NULL)
615
		usage(E_OPTERR);
616
	    if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0) {
617 618
		fprintf(stderr, "io_addr: SIOCGIFMAP: %s\n", strerror(errno));
		spp++;
619 620 621 622 623
		goterr = 1;
		continue;
	    }
	    ifr.ifr_map.base_addr = strtol(*spp, NULL, 0);
	    if (ioctl(skfd, SIOCSIFMAP, &ifr) < 0) {
624
		fprintf(stderr, "io_addr: SIOCSIFMAP: %s\n", strerror(errno));
625 626 627 628 629 630 631
		goterr = 1;
	    }
	    spp++;
	    continue;
	}
	if (!strcmp(*spp, "irq")) {
	    if (*++spp == NULL)
632
		usage(E_OPTERR);
633
	    if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0) {
634
		fprintf(stderr, "irq: SIOCGIFMAP: %s\n", strerror(errno));
635
		goterr = 1;
636
		spp++;
637 638 639 640
		continue;
	    }
	    ifr.ifr_map.irq = atoi(*spp);
	    if (ioctl(skfd, SIOCSIFMAP, &ifr) < 0) {
641
		fprintf(stderr, "irq: SIOCSIFMAP: %s\n", strerror(errno));
642 643 644 645 646 647 648 649
		goterr = 1;
	    }
	    spp++;
	    continue;
	}
	if (!strcmp(*spp, "-pointopoint")) {
	    goterr |= clr_flag(ifr.ifr_name, IFF_POINTOPOINT);
	    spp++;
650
	    if (test_flag(ifr.ifr_name, IFF_POINTOPOINT) > 0)
651
	    	fprintf(stderr, _("Warning: Interface %s still in POINTOPOINT mode.\n"), ifr.ifr_name);
652 653 654 655 656
	    continue;
	}
	if (!strcmp(*spp, "pointopoint")) {
	    if (*(spp + 1) != NULL) {
		spp++;
657
		safe_strncpy(host, *spp, (sizeof host));
658
		if (ap->input(0, host, &_sa)) {
659 660 661 662
		    if (ap->herror)
		    	ap->herror(host);
		    else
		    	fprintf(stderr, _("ifconfig: Error resolving '%s' for pointopoint\n"), host);
663 664 665 666
		    goterr = 1;
		    spp++;
		    continue;
		}
667
		memcpy(&ifr.ifr_dstaddr, sa, sizeof(struct sockaddr));
668
		if (ioctl(ap->fd, SIOCSIFDSTADDR, &ifr) < 0) {
669 670 671 672 673 674 675 676 677 678 679 680
		    fprintf(stderr, "SIOCSIFDSTADDR: %s\n",
			    strerror(errno));
		    goterr = 1;
		}
	    }
	    goterr |= set_flag(ifr.ifr_name, IFF_POINTOPOINT);
	    spp++;
	    continue;
	};

	if (!strcmp(*spp, "hw")) {
	    if (*++spp == NULL)
681
		usage(E_OPTERR);
682
	    if ((hw = get_hwtype(*spp)) == NULL)
683
		usage(E_OPTERR);
684 685 686 687 688 689
	    if (hw->input == NULL) {
	    	fprintf(stderr, _("hw address type `%s' has no handler to set address. failed.\n"), *spp);
	    	spp+=2;
	    	goterr = 1;
	    	continue;
	    }
690
	    if (*++spp == NULL)
691
		usage(E_OPTERR);
692
	    safe_strncpy(host, *spp, (sizeof host));
693
	    if (hw->input(host, &_sa) < 0) {
694 695 696 697 698
		fprintf(stderr, _("%s: invalid %s address.\n"), host, hw->name);
		goterr = 1;
		spp++;
		continue;
	    }
699
	    memcpy(&ifr.ifr_hwaddr, sa, sizeof(struct sockaddr));
700
	    if (ioctl(skfd, SIOCSIFHWADDR, &ifr) < 0) {
701 702 703 704 705 706
		if (errno == EBUSY)
			fprintf(stderr, "SIOCSIFHWADDR: %s - you may need to down the interface\n",
				strerror(errno));
		else
			fprintf(stderr, "SIOCSIFHWADDR: %s\n",
				strerror(errno));
707 708 709 710
		goterr = 1;
	    }
	    spp++;
	    continue;
Phil Blundell's avatar
Phil Blundell committed
711
	}
712
#if HAVE_AFINET || HAVE_AFINET6
713 714
	if (!strcmp(*spp, "add")) {
	    if (*++spp == NULL)
715
		usage(E_OPTERR);
716 717 718 719 720 721
#if HAVE_AFINET6
	    if (strchr(*spp, ':')) {
		/* INET6 */
		if ((cp = strchr(*spp, '/'))) {
		    prefix_len = atol(cp + 1);
		    if ((prefix_len < 0) || (prefix_len > 128))
722
			usage(E_OPTERR);
723 724
		    *cp = 0;
		} else {
725
		    prefix_len = 128;
726 727
		}
		safe_strncpy(host, *spp, (sizeof host));
728
		if (inet6_aftype.input(1, host, &_sa) < 0) {
729 730 731 732
		    if (inet6_aftype.herror)
		    	inet6_aftype.herror(host);
		    else
		    	fprintf(stderr, _("ifconfig: Error resolving '%s' for add\n"), host);
733 734 735 736
		    goterr = 1;
		    spp++;
		    continue;
		}
737
		memcpy(&ifr6.ifr6_addr, &sin6->sin6_addr, sizeof(struct in6_addr));
738

739 740
		fd = get_socket_for_af(AF_INET6);
		if (fd < 0) {
741
		    fprintf(stderr,
742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
			    _("No support for INET6 on this system.\n"));
		    goterr = 1;
		    spp++;
		    continue;
		}
		if (ioctl(fd, SIOGIFINDEX, &ifr) < 0) {
		    perror("SIOGIFINDEX");
		    goterr = 1;
		    spp++;
		    continue;
		}
		ifr6.ifr6_ifindex = ifr.ifr_ifindex;
		ifr6.ifr6_prefixlen = prefix_len;
		if (ioctl(fd, SIOCSIFADDR, &ifr6) < 0) {
		    perror("SIOCSIFADDR");
		    goterr = 1;
		}
759 760 761
		spp++;
		continue;
	    }
762
#endif
763
#if HAVE_AFINET
764
	    { /* ipv4 address a.b.c.d */
765
		in_addr_t ip, nm, bc;
766
		safe_strncpy(host, *spp, (sizeof host));
767
		if (inet_aftype.input(0, host, &_sa) < 0) {
768 769 770 771 772 773 774
		    ap->herror(host);
		    goterr = 1;
		    spp++;
		    continue;
		}
		fd = get_socket_for_af(AF_INET);
		if (fd < 0) {
775
		    fprintf(stderr,
776 777 778 779 780 781
			    _("No support for INET on this system.\n"));
		    goterr = 1;
		    spp++;
		    continue;
		}

782
		memcpy(&ip, &sin->sin_addr.s_addr, sizeof(ip));
783

784 785 786 787 788 789 790 791
		if (get_nmbc_parent(ifr.ifr_name, &nm, &bc) < 0) {
			fprintf(stderr, _("Interface %s not initialized\n"),
				ifr.ifr_name);
			goterr = 1;
			spp++;
			continue;
		}
		set_ifstate(ifr.ifr_name, ip, nm, bc, 1);
792

793 794 795
	    }
	    spp++;
	    continue;
796
#else
Phil Blundell's avatar
Phil Blundell committed
797
	    fprintf(stderr, _("Bad address.\n"));
798
#endif
Phil Blundell's avatar
Phil Blundell committed
799
	}
800 801 802
#endif

#if HAVE_AFINET || HAVE_AFINET6
803 804
	if (!strcmp(*spp, "del")) {
	    if (*++spp == NULL)
805
		usage(E_OPTERR);
806

807 808 809 810 811 812
#ifdef SIOCDIFADDR
#if HAVE_AFINET6
	    if (strchr(*spp, ':')) {	/* INET6 */
		if ((cp = strchr(*spp, '/'))) {
		    prefix_len = atol(cp + 1);
		    if ((prefix_len < 0) || (prefix_len > 128))
813
			usage(E_OPTERR);
814 815
		    *cp = 0;
		} else {
816
		    prefix_len = 128;
817 818
		}
		safe_strncpy(host, *spp, (sizeof host));
819
		if (inet6_aftype.input(1, host, &_sa) < 0) {
820 821 822 823 824
		    inet6_aftype.herror(host);
		    goterr = 1;
		    spp++;
		    continue;
		}
825
		memcpy(&ifr6.ifr6_addr, &sin6->sin6_addr,
826
		       sizeof(struct in6_addr));
827

828 829
		fd = get_socket_for_af(AF_INET6);
		if (fd < 0) {
830
		    fprintf(stderr,
831 832 833 834 835 836 837 838 839 840 841 842 843
			    _("No support for INET6 on this system.\n"));
		    goterr = 1;
		    spp++;
		    continue;
		}
		if (ioctl(fd, SIOGIFINDEX, &ifr) < 0) {
		    perror("SIOGIFINDEX");
		    goterr = 1;
		    spp++;
		    continue;
		}
		ifr6.ifr6_ifindex = ifr.ifr_ifindex;
		ifr6.ifr6_prefixlen = prefix_len;
844 845
		if (opt_v)
			fprintf(stderr, "now deleting: ioctl(SIOCDIFADDR,{ifindex=%d,prefixlen=%ld})\n",ifr.ifr_ifindex,prefix_len);
846 847 848 849 850
		if (ioctl(fd, SIOCDIFADDR, &ifr6) < 0) {
		    fprintf(stderr, "SIOCDIFADDR: %s\n",
			    strerror(errno));
		    goterr = 1;
		}
851 852 853
		spp++;
		continue;
	    }
854
#endif
855
#if HAVE_AFINET
856 857
	    {
		/* ipv4 address a.b.c.d */
858
		in_addr_t ip, nm, bc;
859
		safe_strncpy(host, *spp, (sizeof host));
860
		if (inet_aftype.input(0, host, &_sa) < 0) {
861 862 863 864 865 866 867 868 869 870 871 872
		    ap->herror(host);
		    goterr = 1;
		    spp++;
		    continue;
		}
		fd = get_socket_for_af(AF_INET);
		if (fd < 0) {
		    fprintf(stderr, _("No support for INET on this system.\n"));
		    goterr = 1;
		    spp++;
		    continue;
		}
873

874 875
		/* Clear "ip" in case sizeof(unsigned long) > sizeof(sin.sin_addr.s_addr) */
		ip = 0;
876
		memcpy(&ip, &sin->sin_addr.s_addr, sizeof(ip));
877

878 879 880 881 882 883 884 885 886
		if (get_nmbc_parent(ifr.ifr_name, &nm, &bc) < 0) {
		    fprintf(stderr, _("Interface %s not initialized\n"),
			    ifr.ifr_name);
		    goterr = 1;
		    spp++;
		    continue;
		}
		set_ifstate(ifr.ifr_name, ip, nm, bc, 0);
	    }
887 888
	    spp++;
	    continue;
889
#else
Phil Blundell's avatar
Phil Blundell committed
890
	    fprintf(stderr, _("Bad address.\n"));
891 892
#endif
#else
Phil Blundell's avatar
Phil Blundell committed
893
	    fprintf(stderr, _("Address deletion not supported on this system.\n"));
894
#endif
Phil Blundell's avatar
Phil Blundell committed
895
	}
896 897
#endif
#if HAVE_AFINET6
898 899
	if (!strcmp(*spp, "tunnel")) {
	    if (*++spp == NULL)
900
		usage(E_OPTERR);
901 902 903
	    if ((cp = strchr(*spp, '/'))) {
		prefix_len = atol(cp + 1);
		if ((prefix_len < 0) || (prefix_len > 128))
904
		    usage(E_OPTERR);
905 906
		*cp = 0;
	    } else {
907
		prefix_len = 128;
908
	    }
909
	    safe_strncpy(host, *spp, (sizeof host));
910
	    if (inet6_aftype.input(1, host, &_sa) < 0) {
911 912 913 914 915
		inet6_aftype.herror(host);
		goterr = 1;
		spp++;
		continue;
	    }
916
	    memcpy(&ifr6.ifr6_addr, &sin6->sin6_addr, sizeof(struct in6_addr));
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932

	    fd = get_socket_for_af(AF_INET6);
	    if (fd < 0) {
		fprintf(stderr, _("No support for INET6 on this system.\n"));
		goterr = 1;
		spp++;
		continue;
	    }
	    if (ioctl(fd, SIOGIFINDEX, &ifr) < 0) {
		perror("SIOGIFINDEX");
		goterr = 1;
		spp++;
		continue;
	    }
	    ifr6.ifr6_ifindex = ifr.ifr_ifindex;
	    ifr6.ifr6_prefixlen = prefix_len;
933

934 935 936 937 938 939 940 941
	    if (ioctl(fd, SIOCSIFDSTADDR, &ifr6) < 0) {
		fprintf(stderr, "SIOCSIFDSTADDR: %s\n",
			strerror(errno));
		goterr = 1;
	    }
	    spp++;
	    continue;
	}
Phil Blundell's avatar
Phil Blundell committed
942 943
#endif

944
	/* If the next argument is a valid hostname, assume OK. */
945
	safe_strncpy(host, *spp, (sizeof host));
946

947
	/* FIXME: sa is too small for INET6 addresses, inet6 should use that too,
948 949
	   broadcast is unexpected */
	if (ap->getmask) {
950
	    switch (ap->getmask(host, &_samask, NULL)) {
951
	    case -1:
952
		usage(E_OPTERR);
953 954 955
		break;
	    case 1:
		if (didnetmask)
956
		    usage(E_OPTERR);
957

958 959
		// remeber to set the netmask from samask later
		neednetmask = 1;
960 961
		break;
	    }
962
	}
963 964 965 966
	if (ap->input == NULL) {
	   fprintf(stderr, _("ifconfig: Cannot set address for this protocol family.\n"));
	   exit(1);
	}
967
	if (ap->input(0, host, &_sa) < 0) {
968 969 970 971 972
	    if (ap->herror)
	    	ap->herror(host);
	    else
	    	fprintf(stderr,_("ifconfig: error resolving '%s' to set address for af=%s\n"), host, ap->name); fprintf(stderr,
	    _("ifconfig: `--help' gives usage information.\n")); exit(1);
973
	}
974
	memcpy(&ifr.ifr_addr, sa, sizeof(struct sockaddr));
975 976 977 978 979 980 981 982 983 984 985 986
	{
	    int r = 0;		/* to shut gcc up */
	    switch (ap->af) {
#if HAVE_AFINET
	    case AF_INET:
		fd = get_socket_for_af(AF_INET);
		if (fd < 0) {
		    fprintf(stderr, _("No support for INET on this system.\n"));
		    exit(1);
		}
		r = ioctl(fd, SIOCSIFADDR, &ifr);
		break;
987
#endif
988
#if HAVE_AFECONET
989 990 991 992 993 994 995 996
	    case AF_ECONET:
		fd = get_socket_for_af(AF_ECONET);
		if (fd < 0) {
		    fprintf(stderr, _("No support for ECONET on this system.\n"));
		    exit(1);
		}
		r = ioctl(fd, SIOCSIFADDR, &ifr);
		break;
997
#endif
998 999
	    default:
		fprintf(stderr,
1000
		_("Don't know how to set addresses for family %d.\n"), ap->af);
1001 1002 1003 1004 1005 1006 1007
		exit(1);
	    }
	    if (r < 0) {
		perror("SIOCSIFADDR");
		goterr = 1;
	    }
	}
1008

1009 1010 1011
       /*
        * Don't do the set_flag() if the address is an alias with a - at the
        * end, since it's deleted already! - Roman
1012 1013
        * Same goes if they used address 0.0.0.0 as the kernel uses this to
        * destroy aliases.
1014 1015
        *
        * Should really use regex.h here, not sure though how well it'll go
1016
        * with the cross-platform support etc.
1017 1018 1019 1020
        */
        {
            char *ptr;
            short int found_colon = 0;
1021
            short int bring_up = 1;
1022 1023
            for (ptr = ifr.ifr_name; *ptr; ptr++ )
                if (*ptr == ':') found_colon++;
1024

1025 1026 1027 1028 1029 1030 1031 1032
            if (found_colon) {
                if (ptr[-1] == '-')
                    bring_up = 0;
                else if (ap->af == AF_INET && sin->sin_addr.s_addr == 0)
                    bring_up = 0;
            }

            if (bring_up)
1033 1034 1035
                goterr |= set_flag(ifr.ifr_name, (IFF_UP | IFF_RUNNING));
        }

1036
	spp++;
Phil Blundell's avatar
Phil Blundell committed
1037 1038
    }

1039
    if (neednetmask) {
1040
	goterr |= set_netmask(skfd, &ifr, samask);
1041 1042 1043
	didnetmask++;
    }

1044 1045 1046
    if (opt_v && goterr)
    	fprintf(stderr, _("WARNING: at least one error occured. (%d)\n"), goterr);

1047
    return (goterr);
Phil Blundell's avatar
Phil Blundell committed
1048
}
1049

1050
struct ifcmd {
1051 1052 1053 1054
    int flag;
    unsigned long addr;
    char *base;
    int baselen;
1055
};
1056

1057 1058 1059 1060
static unsigned char searcher[256];

static int set_ip_using(const char *name, int c, unsigned long ip)
{
1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
    struct ifreq ifr;
    struct sockaddr_in sin;

    safe_strncpy(ifr.ifr_name, name, IFNAMSIZ);
    memset(&sin, 0, sizeof(struct sockaddr));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = ip;
    memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
    if (ioctl(skfd, c, &ifr) < 0)
	return -1;
    return 0;
1072
}
1073

1074 1075
static int do_ifcmd(struct interface *x, struct ifcmd *ptr)
{
1076 1077 1078
    char *z, *e;
    struct sockaddr_in *sin;
    int i;
1079

1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
    if (do_if_fetch(x) < 0)
	return 0;
    if (strncmp(x->name, ptr->base, ptr->baselen) != 0)
	return 0; /* skip */
    z = strchr(x->name, ':');
    if (!z || !*z)
	return 0;
    z++;
    for (e = z; *e; e++)
	if (*e == '-') /* deleted */
	    return 0;
    i = atoi(z);
    if (i < 0 || i > 255)
	abort();
    searcher[i] = 1;
1095

1096
    /* copy */
1097
    sin = (struct sockaddr_in *)&x->dstaddr_sas;
1098 1099 1100
    if (sin->sin_addr.s_addr != ptr->addr) {
	return 0;
    }
1101

1102 1103 1104 1105 1106 1107 1108 1109 1110
    if (ptr->flag) {
	/* turn UP */
	if (set_flag(x->name, IFF_UP | IFF_RUNNING) == -1)
	    return -1;
    } else {
	/* turn DOWN */
	if (clr_flag(x->name, IFF_UP) == -1)
	    return -1;
    }
1111

1112
    return 1; /* all done! */
1113 1114 1115 1116
}


static int get_nmbc_parent(char *parent,
1117
			   in_addr_t *nm, in_addr_t *bc)
1118
{
1119 1120
    struct interface *i;
    struct sockaddr_in *sin;
1121

1122 1123 1124 1125
    i = lookup_interface(parent);
    if (!i)
	return -1;
    if (do_if_fetch(i) < 0)
1126
	return 0;
1127
    sin = (struct sockaddr_in *)&i->netmask_sas;
1128
    memcpy(nm, &sin->sin_addr.s_addr, sizeof(*nm));
1129
    sin = (struct sockaddr_in *)&i->broadaddr_sas;
1130
    memcpy(bc, &sin->sin_addr.s_addr, sizeof(*bc));
1131
    return 0;
1132 1133
}

1134
static int set_ifstate(char *parent, in_addr_t ip, in_addr_t nm, in_addr_t bc,
1135 1136
		       int flag)
{
1137 1138 1139
    char buf[IFNAMSIZ];
    struct ifcmd pt;
    int i;
1140

1141 1142 1143 1144 1145
    pt.base = parent;
    pt.baselen = strlen(parent);
    pt.addr = ip;
    pt.flag = flag;
    memset(searcher, 0, sizeof(searcher));
1146
    i = for_all_interfaces((int (*)(struct interface *,void *))do_ifcmd,
1147 1148 1149 1150
			   &pt);
    if (i == -1)
	return -1;
    if (i == 1)
1151
	return 0;
1152

1153 1154 1155 1156 1157 1158 1159
    /* add a new interface */
    for (i = 0; i < 256; i++)
	if (searcher[i] == 0)
	    break;

    if (i == 256)
	return -1; /* FAILURE!!! out of ip addresses */
1160

1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171
    if (snprintf(buf, IFNAMSIZ, "%s:%d", parent, i) > IFNAMSIZ)
	return -1;
    if (set_ip_using(buf, SIOCSIFADDR, ip) == -1)
	return -1;
    if (set_ip_using(buf, SIOCSIFNETMASK, nm) == -1)
	return -1;
    if (set_ip_using(buf, SIOCSIFBRDADDR, bc) == -1)
	return -1;
    if (set_flag(buf, IFF_BROADCAST) == -1)
	return -1;
    return 0;
1172
}