1/* ifconfig 2 * 3 * Similar to the standard Unix ifconfig, but with only the necessary 4 * parts for AF_INET, and without any printing of if info (for now). 5 * 6 * Bjorn Wesen, Axis Communications AB 7 * 8 * 9 * Authors of the original ifconfig was: 10 * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> 11 * 12 * This program is free software; you can redistribute it 13 * and/or modify it under the terms of the GNU General 14 * Public License as published by the Free Software 15 * Foundation; either version 2 of the License, or (at 16 * your option) any later version. 17 * 18 * $Id: ifconfig.c,v 1.1.1.1 2008/10/15 03:28:32 james26_jang Exp $ 19 * 20 */ 21 22/* 23 * Heavily modified by Manuel Novoa III Mar 6, 2001 24 * 25 * From initial port to busybox, removed most of the redundancy by 26 * converting to a table-driven approach. Added several (optional) 27 * args missing from initial port. 28 * 29 * Still missing: media, tunnel. 30 */ 31 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> // strcmp and friends 35#include <ctype.h> // isdigit and friends 36#include <stddef.h> /* offsetof */ 37#include <sys/types.h> 38#include <sys/socket.h> 39#include <sys/ioctl.h> 40#include <netinet/in.h> 41#include <arpa/inet.h> 42#include <net/if.h> 43#include <net/if_arp.h> 44#include <linux/if_ether.h> 45#include "busybox.h" 46 47#ifdef BB_FEATURE_IFCONFIG_SLIP 48#include <linux/if_slip.h> 49#endif 50 51/* I don't know if this is needed for busybox or not. Anyone? */ 52#define QUESTIONABLE_ALIAS_CASE 53 54 55/* Defines for glibc2.0 users. */ 56#ifndef SIOCSIFTXQLEN 57#define SIOCSIFTXQLEN 0x8943 58#define SIOCGIFTXQLEN 0x8942 59#endif 60 61/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */ 62#ifndef ifr_qlen 63#define ifr_qlen ifr_ifru.ifru_mtu 64#endif 65 66#ifndef IFF_DYNAMIC 67#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */ 68#endif 69 70/* 71 * Here are the bit masks for the "flags" member of struct options below. 72 * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'. 73 * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg. 74 */ 75#define N_CLR 0x01 76#define M_CLR 0x02 77#define N_SET 0x04 78#define M_SET 0x08 79#define N_ARG 0x10 80#define M_ARG 0x20 81 82#define M_MASK (M_CLR | M_SET | M_ARG) 83#define N_MASK (N_CLR | N_SET | N_ARG) 84#define SET_MASK (N_SET | M_SET) 85#define CLR_MASK (N_CLR | M_CLR) 86#define SET_CLR_MASK (SET_MASK | CLR_MASK) 87#define ARG_MASK (M_ARG | N_ARG) 88 89/* 90 * Here are the bit masks for the "arg_flags" member of struct options below. 91 */ 92 93/* 94 * cast type: 95 * 00 int 96 * 01 char * 97 * 02 HOST_COPY in_ether 98 * 03 HOST_COPY INET_resolve 99 */ 100#define A_CAST_TYPE 0x03 101/* 102 * map type: 103 * 00 not a map type (mem_start, io_addr, irq) 104 * 04 memstart (unsigned long) 105 * 08 io_addr (unsigned short) 106 * 0C irq (unsigned char) 107 */ 108#define A_MAP_TYPE 0x0C 109#define A_ARG_REQ 0x10 /* Set if an arg is required. */ 110#define A_NETMASK 0x20 /* Set if netmask (check for multiple sets). */ 111#define A_SET_AFTER 0x40 /* Set a flag at the end. */ 112#define A_COLON_CHK 0x80 /* Is this needed? See below. */ 113#define A_HOSTNAME 0x100 /* Set if it is ip addr. */ 114#define A_BROADCAST 0x200 /* Set if it is broadcast addr. */ 115 116/* 117 * These defines are for dealing with the A_CAST_TYPE field. 118 */ 119#define A_CAST_CHAR_PTR 0x01 120#define A_CAST_RESOLVE 0x01 121#define A_CAST_HOST_COPY 0x02 122#define A_CAST_HOST_COPY_IN_ETHER A_CAST_HOST_COPY 123#define A_CAST_HOST_COPY_RESOLVE (A_CAST_HOST_COPY | A_CAST_RESOLVE) 124 125/* 126 * These defines are for dealing with the A_MAP_TYPE field. 127 */ 128#define A_MAP_ULONG 0x04 /* memstart */ 129#define A_MAP_USHORT 0x08 /* io_addr */ 130#define A_MAP_UCHAR 0x0C /* irq */ 131 132/* 133 * Define the bit masks signifying which operations to perform for each arg. 134 */ 135 136#define ARG_METRIC (A_ARG_REQ /*| A_CAST_INT*/) 137#define ARG_MTU (A_ARG_REQ /*| A_CAST_INT*/) 138#define ARG_TXQUEUELEN (A_ARG_REQ /*| A_CAST_INT*/) 139#define ARG_MEM_START (A_ARG_REQ | A_MAP_ULONG) 140#define ARG_IO_ADDR (A_ARG_REQ | A_MAP_USHORT) 141#define ARG_IRQ (A_ARG_REQ | A_MAP_UCHAR) 142#define ARG_DSTADDR (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE) 143#define ARG_NETMASK (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK) 144#define ARG_BROADCAST (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_BROADCAST) 145#define ARG_HW (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER) 146#define ARG_POINTOPOINT (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER) 147#define ARG_KEEPALIVE (A_ARG_REQ | A_CAST_CHAR_PTR) 148#define ARG_OUTFILL (A_ARG_REQ | A_CAST_CHAR_PTR) 149#define ARG_HOSTNAME (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK | A_HOSTNAME) 150 151 152/* 153 * Set up the tables. Warning! They must have corresponding order! 154 */ 155 156struct arg1opt { 157 const char *name; 158 unsigned short selector; 159 unsigned short ifr_offset; 160}; 161 162struct options { 163 const char *name; 164 const unsigned char flags; 165 const unsigned int arg_flags; 166 const unsigned short selector; 167}; 168 169#define ifreq_offsetof(x) offsetof(struct ifreq, x) 170 171static const struct arg1opt Arg1Opt[] = { 172 {"SIOCSIFMETRIC", SIOCSIFMETRIC, ifreq_offsetof(ifr_metric)}, 173 {"SIOCSIFMTU", SIOCSIFMTU, ifreq_offsetof(ifr_mtu)}, 174 {"SIOCSIFTXQLEN", SIOCSIFTXQLEN, ifreq_offsetof(ifr_qlen)}, 175 {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)}, 176 {"SIOCSIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask)}, 177 {"SIOCSIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr)}, 178#ifdef BB_FEATURE_IFCONFIG_HW 179 {"SIOCSIFHWADDR", SIOCSIFHWADDR, ifreq_offsetof(ifr_hwaddr)}, 180#endif 181 {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)}, 182#ifdef SIOCSKEEPALIVE 183 {"SIOCSKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data)}, 184#endif 185#ifdef SIOCSOUTFILL 186 {"SIOCSOUTFILL", SIOCSOUTFILL, ifreq_offsetof(ifr_data)}, 187#endif 188#ifdef BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ 189 {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.mem_start)}, 190 {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.base_addr)}, 191 {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.irq)}, 192#endif 193 /* Last entry if for unmatched (possibly hostname) arg. */ 194 {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)}, 195}; 196 197static const struct options OptArray[] = { 198 {"metric", N_ARG, ARG_METRIC, 0}, 199 {"mtu", N_ARG, ARG_MTU, 0}, 200 {"txqueuelen", N_ARG, ARG_TXQUEUELEN, 0}, 201 {"dstaddr", N_ARG, ARG_DSTADDR, 0}, 202 {"netmask", N_ARG, ARG_NETMASK, 0}, 203 {"broadcast", N_ARG | M_CLR, ARG_BROADCAST, IFF_BROADCAST}, 204#ifdef BB_FEATURE_IFCONFIG_HW 205 {"hw", N_ARG, ARG_HW, 0}, 206#endif 207 {"pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT}, 208#ifdef SIOCSKEEPALIVE 209 {"keepalive", N_ARG, ARG_KEEPALIVE, 0}, 210#endif 211#ifdef SIOCSOUTFILL 212 {"outfill", N_ARG, ARG_OUTFILL, 0}, 213#endif 214#ifdef BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ 215 {"mem_start", N_ARG, ARG_MEM_START, 0}, 216 {"io_addr", N_ARG, ARG_IO_ADDR, 0}, 217 {"irq", N_ARG, ARG_IRQ, 0}, 218#endif 219 {"arp", N_CLR | M_SET, 0, IFF_NOARP}, 220 {"trailers", N_CLR | M_SET, 0, IFF_NOTRAILERS}, 221 {"promisc", N_SET | M_CLR, 0, IFF_PROMISC}, 222 {"multicast", N_SET | M_CLR, 0, IFF_MULTICAST}, 223 {"allmulti", N_SET | M_CLR, 0, IFF_ALLMULTI}, 224 {"dynamic", N_SET | M_CLR, 0, IFF_DYNAMIC}, 225 {"up", N_SET , 0, (IFF_UP | IFF_RUNNING)}, 226 {"down", N_CLR , 0, IFF_UP}, 227 { NULL, 0, ARG_HOSTNAME, (IFF_UP | IFF_RUNNING)} 228}; 229 230/* 231 * A couple of prototypes. 232 */ 233 234#ifdef BB_FEATURE_IFCONFIG_HW 235static int in_ether(char *bufp, struct sockaddr *sap); 236#endif 237 238#ifdef BB_FEATURE_IFCONFIG_STATUS 239extern int interface_opt_a; 240extern int display_interfaces(char *ifname); 241#endif 242 243/* 244 * Our main function. 245 */ 246 247int ifconfig_main(int argc, char **argv) 248{ 249 struct ifreq ifr; 250 struct sockaddr_in sai; 251 struct sockaddr_in sai_hostname, sai_netmask; 252#ifdef BB_FEATURE_IFCONFIG_HW 253 struct sockaddr sa; 254#endif 255 const struct arg1opt *a1op; 256 const struct options *op; 257 int sockfd; /* socket fd we use to manipulate stuff with */ 258 int goterr; 259 int selector; 260 char *p; 261 char host[128]; 262 unsigned int mask; 263 unsigned int did_flags; 264 265 goterr = 0; 266 did_flags = 0; 267 268 /* skip argv[0] */ 269 ++argv; 270 --argc; 271 272#ifdef BB_FEATURE_IFCONFIG_STATUS 273 if ((argc > 0) && (strcmp(*argv,"-a") == 0)) { 274 interface_opt_a = 1; 275 --argc; 276 ++argv; 277 } 278#endif 279 280 if(argc <= 1) { 281#ifdef BB_FEATURE_IFCONFIG_STATUS 282 return display_interfaces(argc ? *argv : NULL); 283#else 284 error_msg_and_die( "ifconfig was not compiled with interface status display support."); 285#endif 286 } 287 288 /* Create a channel to the NET kernel. */ 289 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 290 perror_msg_and_die("socket"); 291 } 292 293 /* get interface name */ 294 safe_strncpy(ifr.ifr_name, *argv, IFNAMSIZ); 295 296 /* Process the remaining arguments. */ 297 while (*++argv != (char *) NULL) { 298 p = *argv; 299 mask = N_MASK; 300 if (*p == '-') { /* If the arg starts with '-'... */ 301 ++p; /* advance past it and */ 302 mask = M_MASK; /* set the appropriate mask. */ 303 } 304 for (op = OptArray ; op->name ; op++) { /* Find table entry. */ 305 if (strcmp(p,op->name) == 0) { /* If name matches... */ 306 if ((mask &= op->flags)) { /* set the mask and go. */ 307 goto FOUND_ARG;; 308 } 309 /* If we get here, there was a valid arg with an */ 310 /* invalid '-' prefix. */ 311 ++goterr; 312 goto LOOP; 313 } 314 } 315 316 /* We fell through, so treat as possible hostname. */ 317 a1op = Arg1Opt + (sizeof(Arg1Opt) / sizeof(Arg1Opt[0])) - 1; 318 mask = op->arg_flags; 319 goto HOSTNAME; 320 321 FOUND_ARG: 322 if (mask & ARG_MASK) { 323 mask = op->arg_flags; 324 a1op = Arg1Opt + (op - OptArray); 325 if (mask & A_NETMASK & did_flags) { 326 show_usage(); 327 } 328 if (*++argv == NULL) { 329 if (mask & A_ARG_REQ) { 330 show_usage(); 331 } else { 332 --argv; 333 mask &= A_SET_AFTER; /* just for broadcast */ 334 } 335 } else { /* got an arg so process it */ 336 HOSTNAME: 337 did_flags |= (mask & (A_NETMASK|A_HOSTNAME)); 338 if (mask & A_CAST_HOST_COPY) { 339#ifdef BB_FEATURE_IFCONFIG_HW 340 if (mask & A_CAST_RESOLVE) { 341#endif 342 safe_strncpy(host, *argv, (sizeof host)); 343 sai.sin_family = AF_INET; 344 sai.sin_port = 0; 345 if (!strcmp(host, "default")) { 346 /* Default is special, meaning 0.0.0.0. */ 347 sai.sin_addr.s_addr = INADDR_ANY; 348 } else if ((!strcmp(host, "+")) && (mask & A_BROADCAST) && 349 (did_flags & (A_NETMASK|A_HOSTNAME))) { 350 /* + is special, meaning broadcast is derived. */ 351 sai.sin_addr.s_addr = (~sai_netmask.sin_addr.s_addr) | 352 (sai_hostname.sin_addr.s_addr & sai_netmask.sin_addr.s_addr); 353 } else if (inet_aton(host, &sai.sin_addr) == 0) { 354 /* It's not a dotted quad. */ 355 ++goterr; 356 continue; 357 } 358 if(mask & A_HOSTNAME) 359 sai_hostname = sai; 360 if(mask & A_NETMASK) 361 sai_netmask = sai; 362 p = (char *) &sai; 363#ifdef BB_FEATURE_IFCONFIG_HW 364 } else { /* A_CAST_HOST_COPY_IN_ETHER */ 365 /* This is the "hw" arg case. */ 366 if (strcmp("ether", *argv) || (*++argv == NULL)) { 367 show_usage(); 368 } 369 safe_strncpy(host, *argv, (sizeof host)); 370 if (in_ether(host, &sa)) { 371 fprintf(stderr, "invalid hw-addr %s\n", host); 372 ++goterr; 373 continue; 374 } 375 p = (char *) &sa; 376 } 377#endif 378 memcpy((((char *)(&ifr)) + a1op->ifr_offset), 379 p, sizeof(struct sockaddr)); 380 } else { 381 unsigned int i = strtoul(*argv,NULL,0); 382 p = ((char *)(&ifr)) + a1op->ifr_offset; 383#ifdef BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ 384 if (mask & A_MAP_TYPE) { 385 if (ioctl(sockfd, SIOCGIFMAP, &ifr) < 0) { 386 ++goterr; 387 continue; 388 } 389 if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR) { 390 *((unsigned char *) p) = i; 391 } else if (mask & A_MAP_USHORT) { 392 *((unsigned short *) p) = i; 393 } else { 394 *((unsigned long *) p) = i; 395 } 396 } else 397#endif 398 if (mask & A_CAST_CHAR_PTR) { 399 *((caddr_t *) p) = (caddr_t) i; 400 } else { /* A_CAST_INT */ 401 *((int *) p) = i; 402 } 403 } 404 405 if (ioctl(sockfd, a1op->selector, &ifr) < 0) { 406 perror(a1op->name); 407 ++goterr; 408 continue; 409 } 410 411#ifdef QUESTIONABLE_ALIAS_CASE 412 if (mask & A_COLON_CHK) { 413 /* 414 * Don't do the set_flag() if the address is an alias with 415 * a - at the end, since it's deleted already! - Roman 416 * 417 * Should really use regex.h here, not sure though how well 418 * it'll go with the cross-platform support etc. 419 */ 420 char *ptr; 421 short int found_colon = 0; 422 for (ptr = ifr.ifr_name; *ptr; ptr++ ) { 423 if (*ptr == ':') { 424 found_colon++; 425 } 426 } 427 428 if (found_colon && *(ptr - 1) == '-') { 429 continue; 430 } 431 } 432#endif 433 } 434 if (!(mask & A_SET_AFTER)) { 435 continue; 436 } 437 mask = N_SET; 438 } 439 440 if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { 441 perror("SIOCGIFFLAGS"); 442 ++goterr; 443 } else { 444 selector = op->selector; 445 if (mask & SET_MASK) { 446 ifr.ifr_flags |= selector; 447 } else { 448 ifr.ifr_flags &= ~selector; 449 } 450 if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { 451 perror("SIOCSIFFLAGS"); 452 ++goterr; 453 } 454 } 455 LOOP: 456 } /* end of while-loop */ 457 458 return goterr; 459} 460 461#ifdef BB_FEATURE_IFCONFIG_HW 462/* Input an Ethernet address and convert to binary. */ 463static int 464in_ether(char *bufp, struct sockaddr *sap) 465{ 466 unsigned char *ptr; 467 int i, j; 468 unsigned char val; 469 unsigned char c; 470 471 sap->sa_family = ARPHRD_ETHER; 472 ptr = sap->sa_data; 473 474 for (i = 0 ; i < ETH_ALEN ; i++) { 475 val = 0; 476 477 /* We might get a semicolon here - not required. */ 478 if (i && (*bufp == ':')) { 479 bufp++; 480 } 481 482 for (j=0 ; j<2 ; j++) { 483 c = *bufp; 484 if (c >= '0' && c <= '9') { 485 c -= '0'; 486 } else if (c >= 'a' && c <= 'f') { 487 c -= ('a' - 10); 488 } else if (c >= 'A' && c <= 'F') { 489 c -= ('A' - 10); 490 } else if (j && (c == ':' || c == 0)) { 491 break; 492 } else { 493 return -1; 494 } 495 ++bufp; 496 val <<= 4; 497 val += c; 498 } 499 *ptr++ = val; 500 } 501 502 return (int) (*bufp); /* Error if we don't end at end of string. */ 503} 504#endif 505