네트워크 인터페이스에 관한 정보를 알고 싶다면 ioctl() 시스템콜을 사용할 수 있습니다.

 

전형적인 운영체제는 2계층으로 나뉩니다. User Space(사용자 모드)Kenel Space(커널 모드).

User Space에 상주하는 애플리케이션이 Kenel Space에 존재하는 자원을 사용하고자할 땐 시스템콜을 이용합니다.

참고로, System Call Function은 내부적으로 System Call Vector와 대응됩니다. 예를들면, exit()함수는 System Call Vector Number: 1번

write()함수는 System Call Vector Number: 4번.

전형적인 운영체제의 커널은 수 백개의 System Call Vector를 제공합니다.

ioctl() 시스템콜은 디바이스 드라이버(Device Driver)와 통신하기위한 함수입니다.

 

디바이스 드라이버(Device Driver)?

아래 2가지 사실을 생각해봅시다.

   1. 하드웨어 주변 장치는 커널 안에 직접적으로 주소지정을 할 수 있다.

   2. 응용 프로그램이 장치와 통신하길 원할 수 있다.( 하드웨어 장치의 기능을 사용하고 싶을 때 )

그런데 커널 개발자는 어떤 장치가 커널 안에 주소를 할당받을지, 그 장치에는 어떤 기능이 있는지 알 지 못합니다.( 미리 예측할 수 없는 문제 )

이러한 문제점 때문에 확장가능한 모듈식으로 커널을 유연하게 설계할 필요가 있었고, 그렇게해서 나온게 디바이스 드라이버입니다.

즉, 어떠한 장치가 연결될지 모르는 상황에서 디바이스 드라이버라는 하나의 중간 계층(?)을 둠으로써 문제를 해결할 수 있었습니다.

 

ioctl() 함수는 struct ifreq 구조체를 매개변수로 받아 정보를 해당 구조체에 저장합니다. 다음 예제는 자신의 MAC Address 정보를 가져옵니다.

#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>

int main(int argc, char* argv[]) {
    struct ifreq ifr;
    u_char* macAddress;
    u_char* interface = argv[1];  // argv[1]은 네트워크 인터페이스라 가정합니다. 예를 들어, ens33, eth0 eth1 ...
    
    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    
    ifr.ifr_addr.sa_family = AF_INET;
    strncpy(ifr.ifr_name, interface, IFNAMSIZ - 1); // ioctl의 대상으로 삼고자하는 인터페이스 이름을 ifr_name 멤버에 저장
    
    ioctl(sock, SIOCGIFCONF, &ifr); // ioctl에 필요한 정보를 struct ifreq 구조체 데이터로 넘깁니다.
    macAddress = (u_char*)ifr.ifr_hwaddr.sa_data;  // ifr_hwaddr 멤버에 하드웨어 주소에 대한 정보가 담겨있습니다.
    
    close(sock);
    return 0;
}

 

다음은 struct ifreq 구조체의 구조입니다.

struct ifreq
  {
# define IFHWADDRLEN	6
# define IFNAMSIZ	IF_NAMESIZE
    union
      {
	char ifrn_name[IFNAMSIZ];	/* Interface name, e.g. "en0".  */
      } ifr_ifrn;

    union
      {
	struct sockaddr ifru_addr;
	struct sockaddr ifru_dstaddr;
	struct sockaddr ifru_broadaddr;
	struct sockaddr ifru_netmask;
	struct sockaddr ifru_hwaddr;
	short int ifru_flags;
	int ifru_ivalue;
	int ifru_mtu;
	struct ifmap ifru_map;
	char ifru_slave[IFNAMSIZ];	/* Just fits the size */
	char ifru_newname[IFNAMSIZ];
	__caddr_t ifru_data;
      } ifr_ifru;
  };
# define ifr_name	ifr_ifrn.ifrn_name	/* interface name 	*/
# define ifr_hwaddr	ifr_ifru.ifru_hwaddr	/* MAC address 		*/
# define ifr_addr	ifr_ifru.ifru_addr	/* address		*/
# define ifr_dstaddr	ifr_ifru.ifru_dstaddr	/* other end of p-p lnk	*/
# define ifr_broadaddr	ifr_ifru.ifru_broadaddr	/* broadcast address	*/
# define ifr_netmask	ifr_ifru.ifru_netmask	/* interface net mask	*/
# define ifr_flags	ifr_ifru.ifru_flags	/* flags		*/
# define ifr_metric	ifr_ifru.ifru_ivalue	/* metric		*/
# define ifr_mtu	ifr_ifru.ifru_mtu	/* mtu			*/
# define ifr_map	ifr_ifru.ifru_map	/* device map		*/
# define ifr_slave	ifr_ifru.ifru_slave	/* slave device		*/
# define ifr_data	ifr_ifru.ifru_data	/* for use by interface	*/
# define ifr_ifindex	ifr_ifru.ifru_ivalue    /* interface index      */
# define ifr_bandwidth	ifr_ifru.ifru_ivalue	/* link bandwidth	*/
# define ifr_qlen	ifr_ifru.ifru_ivalue	/* queue length		*/
# define ifr_newname	ifr_ifru.ifru_newname	/* New name		*/
# define _IOT_ifreq	_IOT(_IOTS(char),IFNAMSIZ,_IOTS(char),16,0,0)
# define _IOT_ifreq_short _IOT(_IOTS(char),IFNAMSIZ,_IOTS(short),1,0,0)
# define _IOT_ifreq_int	_IOT(_IOTS(char),IFNAMSIZ,_IOTS(int),1,0,0)