■ 인터페이스 일반정보 얻기


인터페이스 일반정보 얻기
ioctl함수는 입출력 장치를 제어하고, 장비를 얻기 위한 목적으로 사용하는 함수다. ioctl을 통해서 해당 장치에 대한 제어와 정보를 얻기 위해서는 해당 사항에 대해서 어떠한 flags를 사용하는지를 알고 있어야 한다.
이들 flags는 /usr/include/bits/ioctls.h에 정의되어 있다. 이중 네트워크 장치들은 SIOCGIF의 접두사를 가지고 정의되어 있다. 다음은 그중 일부분이다.

#define SIOCGIFNAME     0x8910          /* get iface name               */
#define SIOCSIFLINK     0x8911          /* set iface channel            */
#define SIOCGIFCONF     0x8912          /* get iface list               */
#define SIOCGIFFLAGS    0x8913          /* get flags                    */
#define SIOCSIFFLAGS    0x8914          /* set flags                    */
#define SIOCGIFADDR     0x8915          /* get PA address               */
#define SIOCSIFADDR     0x8916          /* set PA address               */
#define SIOCGIFDSTADDR  0x8917          /* get remote PA address        */
#define SIOCSIFDSTADDR  0x8918          /* set remote PA address        */
#define SIOCGIFBRDADDR  0x8919          /* get broadcast PA address     */
#define SIOCSIFBRDADDR  0x891a          /* set broadcast PA address     */
#define SIOCGIFNETMASK  0x891b          /* get network PA mask          */
#define SIOCSIFNETMASK  0x891c          /* set network PA mask          */
#define SIOCGIFMETRIC   0x891d          /* get metric                   */
#define SIOCSIFMETRIC   0x891e          /* set metric                   */
#define SIOCGIFMEM      0x891f          /* get memory address (BSD)     */
#define SIOCSIFMEM      0x8920          /* set memory address (BSD)     */
#define SIOCGIFMTU      0x8921          /* get MTU size                 */
#define SIOCSIFMTU      0x8922          /* set MTU size                 */
#define SIOCSIFNAME     0x8923          /* set interface name           */
#
define SIOCSIFHWADDR   0x8924          /* set hardware address         */
이름으로 어디에 사용되는 플레그들인지 충분히 알아볼 수 있으리라 생각된다.


■ ioctl을 이용한 인터페이스 정보 수집


다음은 ioctl함수를 이용해서 eth0의 인터페이스 정보를 수집하는 방법이다.
int fd;
struct ifreq ifrq;
struct sockaddr_in *sin;

fd = socket(AF_INET, SOCK_DGRAM, 0);

// eth0에 대한 MAC 주소를 얻는다.
strcpy(ifrq.ifr_name, "eth0");
if (ioctl(fd,SIOCGIFHWADDR, &ifrq) < 0)  
{
    // 에러처리
}
printf("Mac Address : %s", ifrq.ifr_hwaddr.sa_data);
// MAC주소를 ":"로 구분되는 표준포멧으로 만들기 위해서는 
// 별도의 코드가 필요하다.

// eth0에 대한 IP address 를 얻는다.
if (ioctl(fd, SIOCGIFADDR, &ifrq) < 0) 
{
    // 에러처리
}
sin = (sockaddr_in *)&ifrq.ifr_addr;
printf("%s\n", inet_ntoa(sin->sin_addr));

코드를 명확히 이해하고 사용하기 위해서는 ifreq 구조체에 대해서 정확한 정보를 가지고 있을 필요가 있다. ifreq 구조체는 /usr/include/net/if.h에 정의되어 있으니 참고하기 바란다.


■ 인터페이스 상태

인터페이스는 여러가지 상태가 있을 수 있다. 현재 up상태가 아닐 수도 있고, loopback 상태의 장치일 수도 있다. 이러한 정보는 SIOCGIFFLAGS를 이용해서 flag값을 분석함으로써 얻어올 수 있다. flags에 관한 정보는 /usr/include/net/if.h에 정의되어 있으니 참고하기 바란다.
#ifdef __USE_MISC
/* Standard interface flags. */
enum
  {
    IFF_UP = 0x1,               /* Interface is up.  */
# define IFF_UP IFF_UP
    IFF_BROADCAST = 0x2,        /* Broadcast address valid.  */
# define IFF_BROADCAST  IFF_BROADCAST
    IFF_DEBUG = 0x4,            /* Turn on debugging.  */
# define IFF_DEBUG      IFF_DEBUG
    IFF_LOOPBACK = 0x8,         /* Is a loopback net.  */
# define IFF_LOOPBACK   IFF_LOOPBACK
    IFF_POINTOPOINT = 0x10,     /* Interface is point-to-point link.  */
# define IFF_POINTOPOINT IFF_POINTOPOINT
    IFF_NOTRAILERS = 0x20,      /* Avoid use of trailers.  */
......

다음은 ioctl를 이용해서 인터페이스 정보를 얻어오는 예제다.
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/stat.h>

#include <arpa/inet.h>

int main(int argc, char **argv)
{
  int fd;
  int i;

  struct ifreq ifrq;
  struct sockaddr_in *sin;

  if (argc != 2)
  {
    printf("usage : ./ifinfo [ifname]\n");
    return 1;
  }
  fd = socket (AF_INET, SOCK_DGRAM, 0);
  if(fd < 0)
  {
    perror("Socket open error");
    return 1;
  }
  strcpy(ifrq.ifr_name, argv[1]);
  if(ioctl(fd, SIOCGIFHWADDR, &ifrq) < 0)
  {
    perror("Mac Error");
  }
  // MAC 주소는 적당히 변환시키도록 하자.
  for (i = 0; i < 6; i++)
  {
    printf("%x\n",ifrq.ifr_addr.sa_data[i]);
  }
  if(ioctl(fd, SIOCGIFADDR, &ifrq) < 0)
  {
    perror("IPADDR Error");
  }
  sin = (struct sockaddr_in *)&ifrq.ifr_addr;
  printf("IPADDR : %s\n", inet_ntoa(sin->sin_addr));

  if(ioctl(fd, SIOCGIFMTU, &ifrq) < 0)
  {
    perror("IPADDR Error");
  }
  printf("MTU : %d\n", ifrq.ifr_mtu);

  // GET NETMASK
  if(ioctl(fd, SIOCGIFNETMASK, &ifrq) < 0)
  {
    perror("NETMASK Error");
  }
  sin = (struct sockaddr_in *)&ifrq.ifr_addr;
  printf("NETMASK : %s\n", inet_ntoa(sin->sin_addr));
  if(ioctl(fd, SIOCGIFFLAGS, &ifrq) < 0)
  {
    perror("SIOCGIFFLAGS");
  }
  if(!(ifrq.ifr_flags & IFF_UP))
  {
    printf("IF STATUS UP\n");
  }
  else
  {
    printf("IF STATUS DOWN\n");
  }
  close(fd);
}
그런데 위의 코드는 약간의 문제가 있다. 그것은 인터페이스 이름을 반드시 알고 있어야만 정보를 얻어올 수 있다는 점이다. 좀더 범용성 있는 코드를 위해서는 현재 사용중인 인터페이스의 이름을 자동으로 얻어와서 거기에 대한 정보를 구조체 형식으로 넘겨주도록 작성되어야 할것이다.

다행히도 ioctl(2)에는 작동중인 인터페이스의 목록을 위한 SIOCGIFCONF를 제공한다. 이 flag를 이용해서 인터페이스의 이름을 포함한 목록을 얻어올 수 있다. SIOCGIFCONF의 응용예는 pcap페이지를 참고하기 바란다.

이상 SIOCGIFCONF를 이용하면 인터페이스의 목록을 얻어올 수 있는데, 작동중인 인터페이스의 목록만을 얻어온다는 문제점을 가진다. 현재 시스템에 몇개의 인터페이스가 있는지, 어떤 종류인지(인터페이스 이름을 보면 기가비트 이더넷인지 아닌지등에 대한 정보를 얻을 수 있다), MAC 등의 정보를 얻기 위해서는 작동중이지 않는 인터페이스라 하더라도 정보를 얻어올 필요가 있다.

이 문제는 /proc/net/dev를 분석함으로써 해결가능하다. dev파일에는 인터페이스의 이름뿐만 아니라 입출력되는 패킷과 데이터의 count정보까지 제공해준다. dev파일에 대한 내용은 뒤에서 따로 다루도록 하겠다.

MAC주소 변환은 char2hex를 참고하기 바란다.


※ 출처 : http://cafe.naver.com/newchany.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=85

by 민트앤라떼 2011. 4. 14. 15:55