开发者

MAC address with getifaddrs

开发者 https://www.devze.com 2023-03-21 11:55 出处:网络
Is there a way to get interface\'s MAC address via getifaddrs() ? I already have this, to get IP addresses, but I have kinda missed MAC. Ive tried to look for the information in getifaddrs(), but the

Is there a way to get interface's MAC address via getifaddrs() ?

I already have this, to get IP addresses, but I have kinda missed MAC. Ive tried to look for the information in getifaddrs(), but there is nothing about MAC addresses

struct ifaddrs *iflist, *iface;

  if (getifaddrs(&i开发者_如何学Pythonflist) < 0) 
  {
      perror("getifaddrs");
  }

  char addrp[INET6_ADDRSTRLEN];
  char macp[INET6_ADDRSTRLEN];
  int i=0;

  for (iface = iflist; iface; iface = iface->ifa_next) 
  {
    int af = iface->ifa_addr->sa_family;
    const void *addr;
    const void *mac;

      switch (af) 
      {
        case AF_INET:
          addr = &((struct sockaddr_in *)iface->ifa_addr)->sin_addr;
          break;
      //get mac address somehow?
        default:
          addr = NULL;
      }

      if (addr) 
      {
        if (inet_ntop(af, addr, addrp, sizeof addrp) == NULL)
        {
           perror("inet_ntop");
           continue;
        }
    if (inet_ntop(af, mac, macp, sizeof macp) == NULL) // this is already for MAC add
        {
           perror("inet_ntop");
           continue;
        }
    if (strcmp(addrp, "127.0.0.1") != 0) 
    {
       strcat(tableO[i].IPaddr, addrp);
       strcat(tableO[i].MACaddr, macp);
       i++;
    }
      }

Thanks


getifaddrs() already provides the MAC address associated with each interface. On Linux, when you bump into a family == AF_PACKET that is the MAC address. Same thing on OSX / BSD but in that case the family will be AF_LINK.


On BSD systems you can use getifaddrs directly for retrieving MACs. Here's an example of the complete macaddr.c tools:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if_dl.h>
#include <ifaddrs.h>

int listmacaddrs(void) {
    struct ifaddrs *ifap, *ifaptr;
    unsigned char *ptr;

    if (getifaddrs(&ifap) == 0) {
        for(ifaptr = ifap; ifaptr != NULL; ifaptr = (ifaptr)->ifa_next) {
            if (((ifaptr)->ifa_addr)->sa_family == AF_LINK) {
                ptr = (unsigned char *)LLADDR((struct sockaddr_dl *)(ifaptr)->ifa_addr);
                printf("%s: %02x:%02x:%02x:%02x:%02x:%02x\n",
                                    (ifaptr)->ifa_name,
                                    *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5));
            }
        }
        freeifaddrs(ifap);
        return 1;
    } else {
        return 0;
    }   
}

int macaddr(char *ifname, char *macaddrstr) {
    struct ifaddrs *ifap, *ifaptr;
    unsigned char *ptr;

    if (getifaddrs(&ifap) == 0) {
        for(ifaptr = ifap; ifaptr != NULL; ifaptr = (ifaptr)->ifa_next) {
            if (!strcmp((ifaptr)->ifa_name, ifname) && (((ifaptr)->ifa_addr)->sa_family == AF_LINK)) {
                ptr = (unsigned char *)LLADDR((struct sockaddr_dl *)(ifaptr)->ifa_addr);
                sprintf(macaddrstr, "%02x:%02x:%02x:%02x:%02x:%02x",
                                    *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5));
                break;
            }
        }
        freeifaddrs(ifap);
        return ifaptr != NULL;
    } else {
        return 0;
    }
}

extern int
main(int argc, char* argv[]) {

    char macaddrstr[18], *ifname;

    if (argc == 2) {
        ifname = argv[1];
        if (!strcmp(ifname,"-l")) {
            return listmacaddrs();
        } else {
            if (macaddr(ifname, macaddrstr)) {
                printf("%s: %s\n", ifname, macaddrstr);
                return 0;
            } else {
                printf("%s: not found\n", ifname);
                return 1;
            }
        }
    } else {
        printf("list all interfaces: %s -l\n", argv[0]);
        printf("single interface: %s interface_name\n", argv[0]);
        return 2;
    }
}


On Linux, you'd do something like this

 case AF_PACKET:  {
            struct sockaddr_ll *s = (struct sockaddr_ll*)iface->ifa_addr;
            int i;
            int len = 0;
            for(i = 0; i < 6; i++)
                len+=sprintf(macp+len,"%02X%s",s->sll_addr[i],i < 5 ? ":":"");

        }

Though, there might be more members of the struct sockaddr_ll you'd want to inspect, see here for a description.


Here is the code for getting IP and MAC addresses

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <net/if.h>

int main(void)
{
  char buf[8192] = {0};
  struct ifconf ifc = {0};
  struct ifreq *ifr = NULL;
  int sck = 0;
  int nInterfaces = 0;
  int i = 0;
  char ip[INET6_ADDRSTRLEN] = {0};
  char macp[19];
  struct ifreq *item;
  struct sockaddr *addr;

  /* Get a socket handle. */
  sck = socket(PF_INET, SOCK_DGRAM, 0);
  if(sck < 0) 
  {
    perror("socket");
    return 1;
  }

  /* Query available interfaces. */
  ifc.ifc_len = sizeof(buf);
  ifc.ifc_buf = buf;
  if(ioctl(sck, SIOCGIFCONF, &ifc) < 0) 
  {
    perror("ioctl(SIOCGIFCONF)");
    return 1;
  }

  /* Iterate through the list of interfaces. */
  ifr = ifc.ifc_req;
  nInterfaces = ifc.ifc_len / sizeof(struct ifreq);

  for(i = 0; i < nInterfaces; i++) 
  {
    item = &ifr[i];

    addr = &(item->ifr_addr);

    /* Get the IP address*/
    if(ioctl(sck, SIOCGIFADDR, item) < 0) 
    {
      perror("ioctl(OSIOCGIFADDR)");
    }

    if (inet_ntop(AF_INET, &(((struct sockaddr_in *)addr)->sin_addr), ip, sizeof ip) == NULL) //vracia adresu interf
        {
           perror("inet_ntop");
           continue;
        }

    /* Get the MAC address */
    if(ioctl(sck, SIOCGIFHWADDR, item) < 0) {
      perror("ioctl(SIOCGIFHWADDR)");
      return 1;
    }

    /* display result */

    sprintf(macp, " %02x:%02x:%02x:%02x:%02x:%02x", 
    (unsigned char)item->ifr_hwaddr.sa_data[0],
    (unsigned char)item->ifr_hwaddr.sa_data[1],
    (unsigned char)item->ifr_hwaddr.sa_data[2],
    (unsigned char)item->ifr_hwaddr.sa_data[3],
    (unsigned char)item->ifr_hwaddr.sa_data[4],
    (unsigned char)item->ifr_hwaddr.sa_data[5]);

    printf("%s %s ", ip, macp);

  }

  return 0;
}


Combining a couple answers, this works on both Linux and Mac/iOS/BSD

#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#ifdef __linux__
    #include <arpa/inet.h>
    #include <netpacket/packet.h>
    #include <net/ethernet.h>
#else
    #include <net/if_dl.h>
#endif

int listmacaddrs(void) {
    struct ifaddrs *ifap, *ifaptr;
    unsigned char *ptr;

    if (getifaddrs(&ifap) == 0) {
        for(ifaptr = ifap; ifaptr != NULL; ifaptr = (ifaptr)->ifa_next) {
            #ifdef __linux__
                char macp[INET6_ADDRSTRLEN];
                if (((ifaptr)->ifa_addr)->sa_family == AF_PACKET) {
                    struct sockaddr_ll *s = (struct sockaddr_ll*)(ifaptr->ifa_addr);
                    int i;
                    int len = 0;
                    for (i = 0; i < 6; i++) {
                        len += sprintf(macp+len, "%02X%s", s->sll_addr[i], i < 5 ? ":":"");
                    }
                    printf("%s: %s\n", (ifaptr)->ifa_name, macp);
                }
            #else
            if (((ifaptr)->ifa_addr)->sa_family == AF_LINK) {
                ptr = (unsigned char *)LLADDR((struct sockaddr_dl *)(ifaptr)->ifa_addr);
                printf("%s: %02x:%02x:%02x:%02x:%02x:%02x\n",
                                    (ifaptr)->ifa_name,
                                    *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5));
            }
            #endif
        }
        freeifaddrs(ifap);
        return 1;
    } else {
        return 0;
    }   
}

int macaddr(char *ifname, char *macaddrstr) {
    struct ifaddrs *ifap, *ifaptr;
    unsigned char *ptr;

    if (getifaddrs(&ifap) == 0) {
        for(ifaptr = ifap; ifaptr != NULL; ifaptr = (ifaptr)->ifa_next) {
            #ifdef __linux__
                if (!strcmp((ifaptr)->ifa_name, ifname) && (((ifaptr)->ifa_addr)->sa_family == AF_PACKET)) {
                    struct sockaddr_ll *s = (struct sockaddr_ll*)(ifaptr->ifa_addr);
                    int i;
                    int len = 0;
                    for (i = 0; i < 6; i++) {
                        len += sprintf(macaddrstr+len, "%02X%s", s->sll_addr[i], i < 5 ? ":":"");
                    }
                    break;
                }
            #else
            if (!strcmp((ifaptr)->ifa_name, ifname) && (((ifaptr)->ifa_addr)->sa_family == AF_LINK)) {
                ptr = (unsigned char *)LLADDR((struct sockaddr_dl *)(ifaptr)->ifa_addr);
                sprintf(macaddrstr, "%02x:%02x:%02x:%02x:%02x:%02x",
                                    *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5));
                break;
            }
            #endif
        }
        freeifaddrs(ifap);
        return ifaptr != NULL;
    } else {
        return 0;
    }
}

extern int
main(int argc, char* argv[]) {

    char macaddrstr[18], *ifname;

    if (argc == 2) {
        ifname = argv[1];
        if (!strcmp(ifname,"-l")) {
            return listmacaddrs();
        } else {
            if (macaddr(ifname, macaddrstr)) {
                printf("%s: %s\n", ifname, macaddrstr);
                return 0;
            } else {
                printf("%s: not found\n", ifname);
                return 1;
            }
        }
    } else {
        printf("list all interfaces: %s -l\n", argv[0]);
        printf("single interface: %s interface_name\n", argv[0]);
        return 2;
    }
}


If you're on Unix, you're looking for ioctl SIOCGIFHWADDR:

SIOCGIFHWADDR, SIOCSIFHWADDR

Get or set the hardware address of a device using ifr_hwaddr. The hardware address is specified in a struct sockaddr. sa_family contains the ARPHRD_* device type, sa_data the L2 hardware address starting from byte 0. Setting the hardware address is a privileged operation.

See man netdevice.


There is my experiments with different approaches to list interfaces

/* Copyright Yadro (C) 2016
 * Author: ed@ngslab.ru
 *
 * For those who have pissed off awking output of ip(8)...
 *
 * [root@opt-03 ~]# ./lsif
 *  1: lo              : 127.0.0.1      : LOOPBACK -
 *  2: eno1            : 172.17.32.102  : ETHER 6c:ae:8b:2c:eb:18
 *  3: br-ctlplane     : 192.0.2.1      : ETHER 6c:ae:8b:2c:eb:19
 *  4: br-ctlplane     : 192.0.2.3      : ETHER 6c:ae:8b:2c:eb:19
 *  5: br-ctlplane     : 192.0.2.2      : ETHER 6c:ae:8b:2c:eb:19
 *
 * See netdevice(7) for
 * - SIOCGIFADDR
 * - SIOCGIFBRDADDR
 * - SIOCGIFCONF (here)
 * - SIOCGIFDSTADDR
 * - SIOCGIFFLAGS
 * - SIOCGIFHWADDR (here)
 * - SIOCGIFINDEX
 * - SIOCGIFMAP
 * - SIOCGIFMETRIC
 * - SIOCGIFMTU
 * - SIOCGIFNAME
 * - SIOCGIFNETMASK
 * - SIOCGIFPFLAGS
 * - SIOCGIFTXQLEN
 */
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <linux/if_link.h>
#include <linux/types.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/ethernet.h> /* the L2 protocols */
#include <arpa/inet.h>
#include <netpacket/packet.h>

/* legacy mode decls */
extern int getopt(int argc, char * const argv[], const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;

/* bool type is not a default feature in C */
typedef enum { false = 0, true = 1 } bool;

static struct {
    int mode;
    bool ifname_set;
    char ifname[IFNAMSIZ+1];
    bool show_ip;
    bool show_mac;
    bool flags_set;
    short flags;
    bool verbose;
} global = {};

int run_ioctl(int opcode, void *arg, const struct sockaddr *sa)
{
    int af = sa ? sa->sa_family : AF_INET;
    int rc, sock = socket(af, SOCK_DGRAM, 0);

    if (sock < 0) {
        if (global.verbose) perror("socket");
        return sock;
    }

    if (sa) {
        int sa_len = sizeof(struct sockaddr);

        switch (af) {
        case AF_INET: sa_len = sizeof(struct sockaddr_in); break;
        case AF_INET6: sa_len = sizeof(struct sockaddr_in6); break;
        case AF_PACKET: sa_len = sizeof(struct sockaddr_ll); break;
        }

        if (bind(sock, sa, sa_len)) {
            // if (global.verbose) perror("bind");
            close(sock);
            return rc;
        }
    }

    rc = ioctl(sock, opcode, arg);
    if (rc) {
        // if (global.verbose) perror("ioctl");
        close(sock);
        return rc;
    }

    close(sock);
    return 0;
}

int query_response_size(void)
{
    struct ifconf ifc = {};
    int rc = run_ioctl(SIOCGIFCONF, &ifc, NULL);

    if (rc) {
        if (global.verbose) perror("query_response_size");
        return rc;
    }

    return ifc.ifc_len;
}

int query_if_hwaddr(struct ifreq *req)
{
    int rc = run_ioctl(SIOCGIFHWADDR, req, NULL);

    if (rc) {
        if (global.verbose) perror("query_if_hwaddr:ioctl");
        return rc;
    }

    return 0;
}

int query_ip_addr(struct ifreq *req)
{
    int rc;

    req->ifr_addr.sa_family = AF_INET;
    rc = run_ioctl(SIOCGIFADDR, req, NULL);

    if (rc) {
        if (global.verbose) perror("query_ip_addr:ioctl");
        return rc;
    }

    return 0;
}

int query_if_flags(struct ifreq *req)
{
    int rc = run_ioctl(SIOCGIFHWADDR, req, NULL);

    if (rc) {
        if (global.verbose) perror("query_if_flags:ioctl");
        return rc;
    }

    return 0;
}

double query_if_last_pkt(const struct sockaddr *sa)
{
    struct timeval t;
    double res;
    int rc = run_ioctl(SIOCGSTAMP, &t, sa);

    if (rc) {
        if (global.verbose) perror("query_if_last_pkt:ioctl");
        return -1.0;
    }

    res = (double) t.tv_sec;
    res += ((double) t.tv_usec) / 1000000.0;

    return res;
}

enum addr_fmt {
    ADDR_FMT_LONG,
    ADDR_FMT_SHORT,
};

static const char iff_flag_delimiter[] = ",";
#define _STR(x) ""#x
#define IFF_FLAG2STR(flag, value, string, descr)            \
    do {                                \
        if ((value) & (IFF_##flag)) {               \
            if ((string)[0])                \
                strcat((string), iff_flag_delimiter);   \
            strcat((string), _STR(flag));           \
        }                           \
    } while (0)

static inline char *if_flags(short flags, enum addr_fmt fmt)
{
    static char sflags[128];

    memset(sflags, 0, sizeof(sflags));
    /* sprintf(sflags, "0x%0*x", (int)sizeof(flags) * 2, flags); */
    IFF_FLAG2STR(UP, flags, sflags, "Interface is up");
    IFF_FLAG2STR(BROADCAST, flags, sflags, "Broadcast address valid");
    IFF_FLAG2STR(DEBUG, flags, sflags, "Turn on debugging");
    IFF_FLAG2STR(LOOPBACK, flags, sflags, "Is a loopback net");
    IFF_FLAG2STR(POINTOPOINT, flags, sflags, "Interface is point-to-point link");
    IFF_FLAG2STR(NOTRAILERS, flags, sflags, "Avoid use of trailers");
    IFF_FLAG2STR(RUNNING, flags, sflags, "Resources allocated");
    IFF_FLAG2STR(NOARP, flags, sflags, "No address resolution protocol");
    IFF_FLAG2STR(PROMISC, flags, sflags, "Receive all packets");
    IFF_FLAG2STR(ALLMULTI, flags, sflags, "Receive all multicast packets");
    IFF_FLAG2STR(MASTER, flags, sflags, "Master of a load balancer");
    IFF_FLAG2STR(SLAVE, flags, sflags, "Slave of a load balancer");
    IFF_FLAG2STR(MULTICAST, flags, sflags, "Supports multicast");
    IFF_FLAG2STR(PORTSEL, flags, sflags, "Can set media type");
    IFF_FLAG2STR(AUTOMEDIA, flags, sflags, "Auto media select active");
    IFF_FLAG2STR(DYNAMIC, flags, sflags, "Dialup device with changing addresses");

    return sflags;
}

static const char *_families[] = {
    [AF_UNSPEC] = "UNSPEC",
    [AF_LOCAL] = "LOCAL", /* == [AF_UNIX] == [AF_FILE] */
    [AF_INET] = "INET",
    [AF_AX25] = "AX25",
    [AF_IPX] = "IPX",
    [AF_APPLETALK] = "APPLETALK",
    [AF_NETROM] = "NETROM",
    [AF_BRIDGE] = "BRIDGE",
    [AF_ATMPVC] = "ATMPVC",
    [AF_X25] = "X25",
    [AF_INET6] = "INET6",
    [AF_ROSE] = "ROSE",
    [AF_DECnet] = "DECnet",
    [AF_NETBEUI] = "NETBEUI",
    [AF_SECURITY] = "SECURITY",
    [AF_KEY] = "KEY",
    [AF_NETLINK] = "NETLINK",
    [AF_ROUTE] = "ROUTE",
    [AF_PACKET] = "PACKET",
    [AF_ASH] = "ASH",
    [AF_ECONET] = "ECONET",
    [AF_ATMSVC] = "ATMSVC",
    [AF_RDS] = "RDS",
    [AF_SNA] = "SNA",
    [AF_IRDA] = "IRDA",
    [AF_PPPOX] = "PPPOX",
    [AF_WANPIPE] = "WANPIPE",
    [AF_LLC] = "LLC",
    [AF_IB] = "IB",
    [AF_MPLS] = "MPLS",
    [AF_CAN] = "CAN",
    [AF_TIPC] = "TIPC",
    [AF_BLUETOOTH] = "BLUETOOTH",
    [AF_IUCV] = "IUCV",
    [AF_RXRPC] = "RXRPC",
    [AF_ISDN] = "ISDN",
    [AF_PHONET] = "PHONET",
    [AF_IEEE802154] = "IEEE802154",
    [AF_CAIF] = "CAIF",
    [AF_ALG] = "ALG",
    [AF_NFC] = "NFC",
    [AF_VSOCK] = "VSOCK",
};

static inline const char *af_name(int af)
{
    if (af >= 0 && af < AF_MAX)
        return _families[af];
    return NULL;
}

static inline char *add_af_name(char *s, int af, enum addr_fmt fmt)
{
    static int af_name_len_max;

    if (!af_name_len_max) {
        int i;

        for (i = 0; i < AF_MAX; i++) {
            int l = strlen(af_name(i));

            if (l > af_name_len_max)
                af_name_len_max = l;
        }
        af_name_len_max++; /* add a space */
    }

    switch (fmt) {
    case ADDR_FMT_LONG:
        sprintf(s, "%-*.*s", af_name_len_max, af_name_len_max, af_name(af));
        break;
    case ADDR_FMT_SHORT:
        strcpy(s, af_name(af));
        strcat(s, " ");
        break;
    }
    s += strlen(s);
    return s;
}

#define min(x,y) (((x) > (y)) ? (y) : (x))

#define left_in(space) (sizeof(space) - strlen(space))

static inline void rpad4fmt(char *s, int s_size, int pad_size, enum addr_fmt fmt)
{
    while (fmt == ADDR_FMT_LONG && strlen(s) < min(s_size, pad_size))
        strncat(s, " ", s_size);
}

static inline int fetch_address(const struct sockaddr *sa, char *s, int size)
{
    int inf_size = -1;

    switch (sa->sa_family) {
    case AF_INET:  inf_size = sizeof(struct sockaddr_in); break;
    case AF_INET6: inf_size = sizeof(struct sockaddr_in6); break;
    }

    return getnameinfo(sa, inf_size, s, size, NULL, 0, NI_NUMERICHOST);
}

static inline void hex_colon_bytes(char *s, const void *data, int d_sz)
{
    int i;
    const uint8_t *d = data;
    char *p;

    for (i = 0, p = s + strlen(s); i < d_sz; i++)
        sprintf(p + i * 3, "%02x%s", d[i], (i < d_sz - 1) ? ":" : "");
}

static inline void decode_packet(char *s, int size, const struct sockaddr *sa, enum addr_fmt fmt)
{
    const struct sockaddr_ll *ll = (struct sockaddr_ll *)sa;
    uint8_t *data = (uint8_t *) &ll->sll_addr[0];
    const int af = ll->sll_hatype;
    char *p;

    switch (af) {
    case ARPHRD_LOOPBACK:
        switch (fmt) {
        case ADDR_FMT_LONG: p = "LOOPBACK -"; break;
        case ADDR_FMT_SHORT:    p = "-"; break;
        }
        strncpy(s, p, size);
        break;
    case ARPHRD_ETHER:
        switch (fmt) {
        case ADDR_FMT_LONG: p = "ETHER %02x:%02x:%02x:%02x:%02x:%02x"; break;
        case ADDR_FMT_SHORT:    p = "%02x:%02x:%02x:%02x:%02x:%02x"; break;
        }
        snprintf(s, size, p, data[0], data[1], data[2], data[3], data[4], data[5]);
        break;
    default:
        switch (fmt) {
        case ADDR_FMT_LONG:
            snprintf(s, size, "<%d> <", af);
            break;
        case ADDR_FMT_SHORT:
            snprintf(s, size, "<%d/", af);
            break;
        }
        hex_colon_bytes(s, data, ll->sll_halen);
        strncat(s, ">", size);
        break;
    }
}

static inline char *ip_addr(const struct sockaddr *sa, enum addr_fmt fmt)
{
    const int af = sa->sa_family;
    static char addr[64];
    char *fmx, *p;
    int i, rc, pad_size;

    addr[0] = '\0';
    p = (fmt == ADDR_FMT_LONG) ? add_af_name(addr, af, fmt) : addr;

    switch (af) {
    case AF_INET:  pad_size = strlen(addr) + 15; break;
    case AF_INET6: pad_size = strlen(addr) + 40; break;
    default: pad_size = 0; break;
    }

    switch (af) {
    case AF_INET:
    case AF_INET6:
        rc = fetch_address(sa, p, min(left_in(addr), NI_MAXHOST));
        if (rc) {
            strcat(addr, "<error:");
            strncat(addr, gai_strerror(rc), sizeof(addr));
            strncat(addr, ">", sizeof(addr));
            return addr;
        }
        rpad4fmt(addr, sizeof(addr), pad_size, fmt);
        break;
    case AF_PACKET: /* no real IP here, of course */
        decode_packet(p, left_in(addr), sa, fmt);
        break;
    default:
        snprintf(p, left_in(addr), "<%d/", af);
        hex_colon_bytes(addr, sa->sa_data, sizeof(sa->sa_data));
        strncat(addr, ">", left_in(addr));
        break;
    }
    return addr;
}

static inline char *hw_addr(const struct sockaddr *sa, enum addr_fmt fmt)
{
    const int af = sa->sa_family;
    uint8_t *data = (uint8_t *) &sa->sa_data[0];
    const size_t data_size = sizeof(sa->sa_data);
    static char mac[64]; /* ETHER aa:bb:cc:dd:ee:ff */
    char *p, *fmx;
    int i;

    switch (af) {
    case ARPHRD_LOOPBACK:
        switch (fmt) {
        case ADDR_FMT_LONG: fmx = "LOOPBACK -"; break;
        case ADDR_FMT_SHORT:    fmx = "-"; break;
        }
        strcpy(mac, fmx);
        break;
    case ARPHRD_ETHER:
        switch (fmt) {
        case ADDR_FMT_LONG: fmx = "ETHER %02x:%02x:%02x:%02x:%02x:%02x"; break;
        case ADDR_FMT_SHORT:    fmx = "%02x:%02x:%02x:%02x:%02x:%02x"; break;
        }
        sprintf(mac, fmx, data[0], data[1], data[2], data[3], data[4], data[5]);
        break;
    default:
        switch (fmt) {
        case ADDR_FMT_LONG:
            sprintf(mac, "<%d> <", af);
            break;
        case ADDR_FMT_SHORT:
            strcpy(mac, "<");
            break;
        }
        for (i = 0, p = mac + strlen(mac); i < data_size; i++)
            sprintf(p + i * 3, "%02x%s", data[i],
                (i < data_size - 1) ? ":" : ">");
        break;
    }

    return mac;
}

static inline void add_ip_addr(void *ifx, const char *errmsg, enum addr_fmt fmt)
{
    struct sockaddr *sa;
    struct ifreq *ifr = ifx;
    struct ifaddrs *ifa = ifx;

    switch (global.mode) {
    case 1: /* already fetched */ sa = &ifr->ifr_addr; break;
    case 2: sa = query_ip_addr(ifr) ? NULL : &ifr->ifr_addr; break;
    case 3: /* already fetched */ sa = ifa->ifa_addr; break;
    }

    fputs(sa ? ip_addr(sa, fmt) : errmsg, stdout);
}

static inline void add_hw_addr(struct ifreq *ifr, const char *errmsg, enum addr_fmt fmt)
{
    fputs(query_if_hwaddr(ifr) ? errmsg : hw_addr(&ifr->ifr_addr, fmt), stdout);
}

static inline void add_flags(void *ifx, const char *errmsg, enum addr_fmt fmt)
{
    struct ifreq *ifr = ifx;
    struct ifaddrs *ifa = ifx;
    int flags = -1;

    switch (global.mode) {
    case 1:
    case 2: flags = query_if_flags(ifr) ? -1 : ifr->ifr_flags; break;
    case 3: /* already fetched */ flags = ifa->ifa_flags; break;
    }

    fputs((flags == -1) ? errmsg : if_flags(flags, fmt), stdout);
}

static inline void add_if_name(void *ifx, enum addr_fmt fmt)
{
    struct ifreq *ifr = ifx;
    struct ifaddrs *ifa = ifx;
    char *name;

    switch (global.mode) {
    case 1:
    case 2: name = ifr->ifr_name; break;
    case 3: name = ifa->ifa_name; break;
    }

    switch (fmt) {
    case ADDR_FMT_LONG:
        printf("%-*.*s", IFNAMSIZ, IFNAMSIZ, name);
        break;
    case ADDR_FMT_SHORT:
        fputs(name, stdout);
        break;
    }
}

static void print_interface(struct ifreq *ifr)
{
    if (global.ifname_set) {
        if (strncmp(ifr->ifr_name, global.ifname, IFNAMSIZ))
            return;
        if (global.show_ip || !global.show_mac) {
            add_ip_addr(ifr, NULL, ADDR_FMT_SHORT);
            fputs(global.show_ip ? "" : "\t", stdout);
        }
        if (global.show_mac || !global.show_ip)
            add_hw_addr(ifr, "<no-hw-addr>", ADDR_FMT_SHORT);
        putchar('\n');
        return;
    }

    add_if_name(ifr, ADDR_FMT_LONG);
    printf(" : ");
    add_ip_addr(ifr, "<error> -", ADDR_FMT_LONG);
    printf(" : ");
    add_hw_addr(ifr, "<error> <no-hw-addr>", ADDR_FMT_LONG);

    if (global.flags_set) {
        printf(" : ");
        add_flags(ifr, "<error-no-flags>", ADDR_FMT_LONG);
    }

    putchar('\n');
}

static void print_address(struct ifaddrs *ifa)
{
    if (global.ifname_set) {
        if (strncmp(ifa->ifa_name, global.ifname, IFNAMSIZ))
            return;
    } else {
        add_if_name(ifa, ADDR_FMT_LONG);
        printf(" : ");
    }
    if (ifa->ifa_addr == NULL) {
        printf("<no-address>\n");
        return;
    }
    add_ip_addr(ifa, "<error> -", ADDR_FMT_LONG);
    if (global.flags_set) {
        printf(" : ");
        add_flags(ifa, "<error-no-flags>", ADDR_FMT_LONG);
    }
    putchar('\n');
    return;
}

static void __dispose_req(struct ifreq **req)
{
    if (req && *req) {
        free(*req);
        *req = 0;
    }
}

static int __1__query_if_list(void)
{
    struct ifreq *req __attribute__((cleanup(__dispose_req))) = NULL;
    struct ifconf ifc = {};
    int rc, i, j, size = query_response_size();

    if (size <= 0) return -1;

    req = calloc(size, 1);
    if (!req) {
        if (global.verbose) perror("query_if_list:cmalloc");
        return -1;
    }

    ifc.ifc_len = size;
    ifc.ifc_req = req;

    rc = run_ioctl(SIOCGIFCONF, &ifc, NULL);
    if (rc) {
        if (global.verbose) perror("query_if_list:ioctl");
        return rc;
    }

    for (i = 0; i < ifc.ifc_len / sizeof(struct ifreq); i++) {
        struct ifreq *ifr = &ifc.ifc_req[i];

        if (global.ifname_set) {
            if (strncmp(ifr->ifr_name, global.ifname, IFNAMSIZ))
                continue;
        } else
            printf("%2d: ", i + 1);
        print_interface(ifr);
    }

    return 0;
}

static void __dispose_if_list_2(struct if_nameindex **if_list)
{
    if (if_list && *if_list) {
        if_freenameindex(*if_list);
        *if_list = 0;
    }
}

static int __2__query_if_list(void)
{
    struct if_nameindex *i;
    struct if_nameindex *if_list
        __attribute__((cleanup(__dispose_if_list_2)))
        = if_nameindex();

    if (!if_list) {
        if (global.verbose) perror("if_nameindex");
        return -1;
    }
    for (i = if_list; ! (i->if_index == 0 && i->if_name == NULL); i++) {
        struct ifreq ifr;

        // printf("%u: %s\n", i->if_index, i->if_name);
        strncpy(ifr.ifr_name, i->if_name, sizeof(ifr.ifr_name));
        print_interface(&ifr);
    }

    return 0;
}

static void __dispose_if_list_3(struct ifaddrs **if_list)
{
    if (if_list && *if_list) {
        freeifaddrs(*if_list);
        *if_list = 0;
    }
}

static int __3__query_if_list(void)
{
    struct ifaddrs *ifa;
    struct ifaddrs *if_list __attribute__((cleanup(__dispose_if_list_3))) = NULL;

    if (getifaddrs(&if_list) == -1) {
        if (global.verbose) perror("getifaddrs");
        return -1;
    }

    for (ifa = if_list; ifa != NULL; ifa = ifa->ifa_next) {
//      if (!ifa->ifa_addr)
//          continue;
        print_address(ifa);
    }
}

int query_if_list(void)
{
    switch (global.mode) {
    case 1: return __1__query_if_list();
    case 2: return __2__query_if_list();
    case 3: return __3__query_if_list();
    }
}

static char HELP[] = "%s [-f] [-i <interface> [-a | -m]]\n\n"
    "Options:\n"
    " -h -- this help\n"
    " -1, -2, -3 -- use different query schemes:\n"
    "   SIOCGIFCONF, if_nameindex, getifaddrs\n"
    " -i <interface> -- set <interface> name to query for\n"
    " -a -- list IP addresses\n"
    " -m -- list MAC addresses (you may want to use 'sort -u') here\n"
    " -f -- add flags to the output\n"
    " -v -- verbose\n"
    "\n";

static int parse_args(int argc, char * const argv[])
{
    const static char optspec[] = "hvi:amf123";

    global.mode = 1; /* default */

    while (true) {
        int opt = getopt(argc, argv, optspec);

        if (opt == -1)
            break;

        switch (opt) {
        case 'h':
            printf(HELP, argv[0]);
            exit(EXIT_SUCCESS);

        case '1': global.mode = 1; break;
        case '2': global.mode = 2; break;
        case '3': global.mode = 3; break;
        case '4': global.mode = 4; break;
        case '5': global.mode = 5; break;
        case '6': global.mode = 6; break;
        case '7': global.mode = 7; break;
        case '8': global.mode = 8; break;
        case '9': global.mode = 9; break;

        case 'i':
            global.ifname_set = true;
            strncpy(global.ifname, optarg, IFNAMSIZ);
            continue;
        case 'a':
            global.show_ip = true;
            continue;
        case 'm':
            global.show_mac = true;
            continue;
        case 'f':
            global.flags_set = true;
            continue;
        case 'v':
            global.verbose = true;
            continue;
        default:
            return -1;
        }
    }

    /* argv[optind] may be parsed here */

    if (optind < argc) {
        fprintf(stderr, "%s: trailing extra args\n", argv[0]);
        return -1;
    }

    if ((global.show_ip || global.show_mac) && !global.ifname_set) {
        fprintf(stderr, "%s: -a and -m require -i <interface>.\n", argv[0]);
        return -1;
    }

    if (global.show_ip && global.show_mac) {
        /* just turn'em off */
        global.show_ip = false;
        global.show_mac = false;
        /*
        fprintf(stderr, "%s: -a and -m are mutually exclusive.\n", argv[0]);
        return -1;
        */
    }

    return 0;
}

int main(int argc, char * const argv[])
{
    if (parse_args(argc, argv))
        return EXIT_FAILURE;

    if (query_if_list())
        return EXIT_FAILURE;

    return EXIT_SUCCESS;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消