others
/* $Id$ */
/* Copyright (c) 2011-2015 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Unix others */
/* 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, version 3 of the License.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <net/if.h>
#include <netinet/in.h>
#if defined(__DeforaOS__)
#elif defined(__FreeBSD__)
# include <ifaddrs.h>
# include <net/if_var.h>
# include <netinet6/in6_var.h>
#elif defined(__NetBSD__)
# include <ifaddrs.h>
# include <net/if.h>
# include <netinet6/in6_var.h>
#endif
#ifndef PROGNAME
# define PROGNAME "ifconfig"
#endif
/* ifconfig */
/* private */
/* types */
typedef int Prefs;
#define PREFS_a 0x1
#define PREFS_m 0x2
typedef struct _idxstr
{
unsigned int idx;
char const * str;
} idxstr;
/* prototypes */
static int _ifconfig(Prefs prefs, int argc, char * argv[]);
static int _ifconfig_error(char const * message, int ret);
static int _ifconfig_usage(void);
static char const * _inet_str(struct sockaddr * addr);
static char const * _mac_media_str(int type);
static char const * _mac_status_str(int state);
/* functions */
/* ifconfig */
static int _ifconfig_all(Prefs prefs);
static int _ifconfig_do(Prefs prefs, char const * name, int argc,
char * argv[]);
static int _ifconfig_show(Prefs prefs, char const * name);
static int _show_mac(Prefs prefs, int fd, struct ifreq * ifr);
#ifdef SIOCGIFFLAGS
static void _show_mac_flags(unsigned short flags);
#endif
static int _mac_media(Prefs prefs, int fd, struct ifreq * ifr);
#ifdef SIOCGIFDATA
static int _mac_status(struct if_data * ifd);
#endif
static int _show_inet(Prefs prefs, int fd, struct ifreq * ifr);
static int _show_inet6(Prefs prefs, char const * name);
#ifdef SIOCGIFDSTADDR_IN6
static int _inet6_do(Prefs prefs, char const * name, int fd,
struct ifaddrs * ifa);
#else
static int _inet6_do(Prefs prefs, char const * name, int fd, void * ifa);
#endif
static void _inet6_print_addr(struct in6_addr * addr);
static int _ifconfig(Prefs prefs, int argc, char * argv[])
{
if(prefs & PREFS_a)
return _ifconfig_all(prefs);
if(argc == 1)
return _ifconfig_show(prefs, argv[0]);
if(argc > 1)
return _ifconfig_do(prefs, argv[0], argc - 1, &argv[1]);
return 0;
}
static int _ifconfig_all(Prefs prefs)
{
struct if_nameindex * ifni;
struct if_nameindex * i;
char const * sep = "";
if((ifni = if_nameindex()) == NULL)
return -_ifconfig_error("if_nameindex", 1);
for(i = ifni; i != NULL && i->if_index != 0; i++)
{
fputs(sep, stderr);
_ifconfig_show(prefs, i->if_name);
sep = "\n";
}
if_freenameindex(ifni);
return 0;
}
static int _ifconfig_do(Prefs prefs, char const * name, int argc,
char * argv[])
{
/* FIXME implement */
return 0;
}
static int _ifconfig_show(Prefs prefs, char const * name)
{
int ret;
int fd;
struct ifreq ifr;
if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
return _ifconfig_error("socket", 1);
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", name);
if((ret = _show_mac(prefs, fd, &ifr)) == 0
&& (ret |= _show_inet(prefs, fd, &ifr)) == 0)
ret |= _show_inet6(prefs, name);
close(fd);
return ret;
}
static int _show_mac(Prefs prefs, int fd, struct ifreq * ifr)
{
#ifdef SIOCGIFDATA
struct ifdatareq ifi;
#endif
printf("%s:", ifr->ifr_name);
#ifdef SIOCGIFFLAGS
if(ioctl(fd, SIOCGIFFLAGS, ifr) != 0)
{
_ifconfig_error(ifr->ifr_name, 1);
if(errno == ENXIO)
return -1;
}
else
_show_mac_flags(ifr->ifr_flags);
#endif
#ifdef SIOCGIFDATA
memcpy(ifi.ifdr_name, ifr->ifr_name, sizeof(ifi.ifdr_name));
if(ioctl(fd, SIOCGIFDATA, &ifi) != 0)
{
_ifconfig_error(ifr->ifr_name, 1);
if(errno == ENXIO)
return -1;
_mac_media(prefs, fd, ifr);
}
else
{
printf(" mtu %lu\n", ifi.ifdr_data.ifi_mtu);
_mac_media(prefs, fd, ifr);
_mac_status(&ifi.ifdr_data);
}
#endif
return 0;
}
#ifdef SIOCGIFFLAGS
static void _show_mac_flags(unsigned short flags)
{
struct
{
int flag;
char const * name;
} names[] =
{
# ifdef IFF_UP
{ IFF_UP, "UP" },
# endif
# ifdef IFF_BROADCAST
{ IFF_BROADCAST, "BROADCAST" },
# endif
# ifdef IFF_DEBUG
{ IFF_DEBUG, "DEBUG" },
# endif
# ifdef IFF_LOOPBACK
{ IFF_LOOPBACK, "LOOPBACK" },
# endif
# ifdef IFF_POINTOPOINT
{ IFF_POINTOPOINT, "POINTOPOINT" },
# endif
# ifdef IFF_RUNNING
{ IFF_RUNNING, "RUNNING" },
# endif
# ifdef IFF_NOARP
{ IFF_NOARP, "NOARP" },
# endif
# ifdef IFF_PROMISC
{ IFF_PROMISC, "PROMISC" },
# endif
# ifdef IFF_OACTIVE
{ IFF_OACTIVE, "OACTIVE" },
# endif
# ifdef IFF_SIMPLEX
{ IFF_SIMPLEX, "SIMPLEX" },
# endif
# ifdef IFF_MULTICAST
{ IFF_MULTICAST, "MULTICAST" },
# endif
};
size_t i;
char const * sep = "";
printf(" flags=%x", flags);
if(sizeof(names) == 0 || flags == 0)
return;
putchar('<');
for(i = 0; i < sizeof(names) / sizeof(*names); i++)
if((flags & names[i].flag) == names[i].flag)
{
printf("%s%s", sep, names[i].name);
sep = ",";
}
putchar('>');
}
#endif
static int _mac_media(Prefs prefs, int fd, struct ifreq * ifr)
{
#ifdef SIOCGIFMEDIA
struct ifmediareq ifm;
memset(&ifm, 0, sizeof(ifm));
memcpy(ifm.ifm_name, ifr->ifr_name, sizeof(ifm.ifm_name));
if(ioctl(fd, SIOCGIFMEDIA, &ifm) != 0)
return (errno != ENOTTY && errno != EINVAL)
? _ifconfig_error("SIOCGIFMEDIA", 1) : 0;
printf("\tmedia: %s\n", _mac_media_str(ifm.ifm_current));
#endif
return 0;
}
#ifdef SIOCGIFDATA
static int _mac_status(struct if_data * ifd)
{
char const * status;
if((status = _mac_status_str(ifd->ifi_link_state)) == NULL)
return 0;
printf("\tstatus: %s\n", status);
return 0;
}
#endif
static int _show_inet(Prefs prefs, int fd, struct ifreq * ifr)
{
if(ioctl(fd, SIOCGIFADDR, ifr) != 0)
{
#ifdef EADDRNOTAVAIL
if(errno == EADDRNOTAVAIL)
return 0;
#endif
#ifdef EAFNOSUPPORT
if(errno == EAFNOSUPPORT)
return 0;
#endif
return -_ifconfig_error("SIOCGIFADDR", 1);
}
printf("%s%s", "\tinet: ", _inet_str(&ifr->ifr_addr));
#ifdef SIOCGIFDSTADDR
if(ioctl(fd, SIOCGIFDSTADDR, ifr) == 0)
printf(" -> %s", _inet_str(&ifr->ifr_dstaddr));
#endif
#ifdef SIOCGIFBRDADDR
if(ioctl(fd, SIOCGIFBRDADDR, ifr) == 0)
printf(" broadcast %s", _inet_str(&ifr->ifr_broadaddr));
#endif
putchar('\n');
return 0;
}
static int _show_inet6(Prefs prefs, char const * name)
{
int ret = 0;
int fd;
#if defined(__FreeBSD__) || defined(__NetBSD__)
struct ifaddrs * ifa;
struct ifaddrs * i;
#endif
if((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
return -_ifconfig_error("socket", 1);
#if !defined(__DeforaOS__) && (defined(__FreeBSD__) || defined(__NetBSD__))
if(getifaddrs(&ifa) != 0)
ret = -_ifconfig_error("getifaddrs", 1);
else
for(i = ifa; i != NULL; i = i->ifa_next)
if(strcmp(i->ifa_name, name) != 0)
continue;
else if(i->ifa_addr->sa_family != AF_INET6)
continue;
else
ret |= _inet6_do(prefs, name, fd, i);
#else
/* FIXME implement */
#endif
close(fd);
return ret;
}
#ifdef SIOCGIFDSTADDR_IN6
static int _inet6_do(Prefs prefs, char const * name, int fd,
struct ifaddrs * ifa)
{
struct in6_ifreq ifr;
if(ifa->ifa_addr->sa_len != sizeof(ifr.ifr_addr))
return -1;
memcpy(&ifr.ifr_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len);
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", name);
printf("%s", "\tinet6: ");
_inet6_print_addr(&ifr.ifr_ifru.ifru_addr.sin6_addr);
if(ioctl(fd, SIOCGIFDSTADDR_IN6, &ifr) == 0)
{
fputs(" -> ", stdout);
_inet6_print_addr(&ifr.ifr_ifru.ifru_dstaddr.sin6_addr);
}
putchar('\n');
#else
static int _inet6_do(Prefs prefs, char const * name, int fd, void * ifa)
{
/* FIXME implement */
#endif
return 0;
}
static void _inet6_print_addr(struct in6_addr * addr)
{
size_t i;
char const * sep = "";
for(i = 0; i < 16; i+=2)
{
printf("%s%02x%02x", sep, addr->s6_addr[i],
addr->s6_addr[i + 1]);
sep = ":";
}
}
/* ifconfig_error */
static int _ifconfig_error(char const * message, int ret)
{
fputs(PROGNAME ": ", stderr);
perror(message);
return ret;
}
/* inet_str */
char const * _inet_str(struct sockaddr * addr)
{
static char buf[16];
if(addr->sa_family != AF_INET)
snprintf(buf, sizeof(buf), "%s", "UNKNOWN");
else
/* FIXME understand why this is so */
snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
(unsigned char)addr->sa_data[2],
(unsigned char)addr->sa_data[3],
(unsigned char)addr->sa_data[4],
(unsigned char)addr->sa_data[5]);
return buf;
}
/* mac_media_str */
static char const * _mac_media_str(int type)
{
static char buf[32];
idxstr is[] =
{
{ 0x20, "Ethernet" },
{ 0x80, "IEEE802.11" },
{ 0, NULL }
};
unsigned int i;
for(i = 0; is[i].str != NULL; i++)
if(is[i].idx == (type & 0xe0))
break;
if(is[i].str == NULL)
{
snprintf(buf, sizeof(buf), "%s (%u)", "UNKNOWN", type);
return buf;
}
snprintf(buf, sizeof(buf), "%s%s", is[i].str, (type & 0x1f) == 0
? " autoselect" : "");
return buf;
}
/* mac_status_str */
static char const * _mac_status_str(int state)
{
switch(state)
{
#ifdef LINK_STATE_DOWN
case LINK_STATE_DOWN: return "inactive";
#endif
#ifdef LINK_STATE_UP
case LINK_STATE_UP: return "active";
#endif
#ifdef LINK_STATE_UNKNOWN
case LINK_STATE_UNKNOWN:
#endif
default: return NULL;
}
}
/* ifconfig_usage */
static int _ifconfig_usage(void)
{
fputs("Usage: " PROGNAME " [-m] interface [argument...]\n"
" " PROGNAME " -a\n", stderr);
return 1;
}
/* public */
/* functions */
/* main */
int main(int argc, char * argv[])
{
int o;
Prefs prefs;
memset(&prefs, 0, sizeof(prefs));
while((o = getopt(argc, argv, "am")) != -1)
switch(o)
{
case 'a':
prefs |= PREFS_a;
break;
case 'm':
prefs |= PREFS_m;
break;
default:
return _ifconfig_usage();
}
if(prefs & PREFS_a)
{
if(optind != argc)
return _ifconfig_usage();
}
else if(optind == argc)
return _ifconfig_usage();
return (_ifconfig(prefs, argc - optind, &argv[optind]) == 0) ? 0 : 2;
}