ioctl Operations

A common use of ioctl by network programs (typically servers) is to obtain information on all the host's interfaces when the program starts: the interface addresses, whether the

interface supports broadcasting, whether the interface supports multicasting, and so on.

#include <unistd.h>
int ioctl(int fd, int request, ... /* void *arg */ );
Returns:0 if OK, -1 on error

We can divide the requests related to networking into six categories:

  1. Socket operations
  2. File operations
  3. Interface operations
  4. ARP cache operations
  5. Routing table operations
  6. STREAMS system

Socket Operations

  1. SIOCATMARK Return through the integer pointed to by the third argument a nonzero value if the socket's read pointer is currently at the out-of-band mark, or a zero value if the read pointer is not at the out-of-band mark.  POSIX replaces this request with the sockatmark function
  2. SIOCGPGRP Return through the integer pointed to by the third argument either the process ID or the process group ID that is set to receive the SIGIO or SIGURG signal for this socket. This request is identical to an fcntl of F_GETOWN, and we note that POSIX standardizes the fcntl.
  3. SIOCSPGRP Set either the process ID or process group ID to receive the SIGIO or SIGURG signal for this socket from the integer pointed to by the third argument. This request is identical to an fcntl of F_SETOWN, and we note that POSIX standardizes the fcntl.

File Operations

  1. FIONBIO  The nonblocking flag for the socket is cleared or turned on, depending on whether the third argument to ioctl points to a zero or nonzero value, respectively. This request has the same effect as the O_NONBLOCK file status flag, which can be set and cleared with the F_SETFL command to the fcntl function.
  2. FIOASYNC The flag that governs the receipt of asynchronous I/O signals (SIGIO) for the socket is cleared or turned on, depending on whether the third argument to
    ioctl points to a zero or nonzero value, respectively. This flag has the same effect as the O_ASYNC file status flag, which can be set and cleared with the F_SETFL command to the fcntl function.
  3. FIONREAD Return in the integer pointed to by the third argument to ioctl the number of bytes currently in the socket receive buffer. This feature also works for files, pipes, and terminals.
  4. FIOSETOWN Equivalent to SIOCSPGRP for a socket
  5. FIOGETOWN Equivalent to SIOCGPGRP for a socket

Interface Configuration

ifconf and ifreq structures used with various interface ioctl requests.

<net/if.h>
struct ifconf {
int ifc_len; /* size of buffer, value-result */
union {
caddr_t ifcu_buf; /* input from user -> kernel */
struct ifreq *ifcu_req; /* return from kernel -> user */
} ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */
#define ifc_req ifc_ifcu.ifcu_req /* array of structures returned */
#define IFNAMSIZ 16
struct ifreq {
char ifr_name[IFNAMSIZ]; /* interface name, e.g., "le0" */
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
short ifru_flags;
int ifru_metric;
caddr_t ifru_data;
} ifr_ifru;
};
#define ifr_addr ifr_ifru.ifru_addr /* address */
#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of
point-to-point link */
#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
#define ifr_flags ifr_ifru.ifru_flags /* flags */
#define ifr_metric ifr_ifru.ifru_metric /* metric */
#define ifr_data ifr_ifru.ifru_data /* for use by interface */

Interface Operations

SIOCGIFADDR Return the unicast address in the ifr_addr member.
SIOCSIFADDR Set the interface address from the ifr_addr member. The initialization function for the interface is also called.
SIOCGIFFLAGS Return the interface flags in the ifr_flags member. The names of the various flags are IFF_xxx and are defined by including the <net/if.h> header. The flags indicate, for example, if the interface is up (IFF_UP), if the interface is a point-to-point interface (IFF_POINTOPOINT), if the interface supports broadcasting  IFF_BROADCAST), and so on.
SIOCSIFFLAGS Set the interface flags from the ifr_flags member.
SIOCGIFDSTADDR Return the point-to-point address in the ifr_dstaddr member.
SIOCSIFDSTADDR Set the point-to-point address from the ifr_dstaddr member.
SIOCGIFBRDADDR Return the broadcast address in the ifr_broadaddr member. The application must first fetch the interface flags and then issue the correct request: SIOCGIFBRDADDR for a broadcast interface or SIOCGIFDSTADDR for a point-to-point interface.
SIOCSIFBRDADDR Set the broadcast address from the ifr_broadaddr member.
SIOCGIFNETMASK Return the subnet mask in the ifr_addr member.
SIOCSIFNETMASK Set the subnet mask from the ifr_addr member.
SIOCGIFMETRIC Return the interface metric in the ifr_metric member. The interface metric is maintained by the kernel for each interface but is used by the routing daemon routed. The interface metric is added to the hop count (to make an interface less favorable).

SIOCGIFADDR Return the unicast address in the ifr_addr member.
SIOCSIFMETRIC Set the interface routing metric from the ifr_metric member

ARP Cache Operations

On some systems, the ARP cache is also manipulated with the ioctl function. Systems that use routing sockets usually use routing sockets instead of ioctl to access the ARP cache. These requests use an arpreq structure, defined by including the <net/if_arp.h> header.

struct arpreq {
struct sockaddr arp_pa; /* protocol address */
struct sockaddr arp_ha; /* hardware address */
int arp_flags; /* flags */
};
#define ATF_INUSE 0x01 /* entry in use */
#define ATF_COM 0x02 /* completed entry (hardware addr valid) */
#define ATF_PERM 0x04 /* permanent entry */
#define ATF_PUBL 0x08 /* published entry (respond for other host) */

The third argument to ioctl must point to one of these structures. The following three requests are supported:

SIOCSARP Add a new entry to the ARP cache or modify an existing entry. arp_pa is an Internet socket address structure containing the IP address, and arp_ha is a
generic socket address structure with sa_family set to AF_UNSPEC and sa_data containing the hardware address (e.g., the 6-byte Ethernet address). The two flags, ATF_PERM and ATF_PUBL, can be specified by the application. The other two flags, ATF_INUSE and ATF_COM, are set by the kernel.
SIOCDARP Delete an entry from the ARP cache. The caller specifies the Internet address for the entry to be deleted.
SIOCGARP Get an entry from the ARP cache. The caller specifies the Internet address, and the corresponding Ethernet address is returned along with the flags.

Only the superuser can add or delete an entry. These three requests are normally issued by the arp program.

Notice that there is no way with ioctl to list all the entries in the ARP cache.

Routing Table Operations

On some systems, two ioctl requests are provided to operate on the routing table. These two requests require that the third argument to ioctl be a pointer to an rtentry
structure, which is defined by including the <net/route.h> header. These requests are normally issued by the route program. Only the superuser can issue these requests. On
systems with routing sockets, these requests use routing sockets instead of ioctl.

SIOCADDRT Add an entry to the routing table.
SIOCDELRT Delete an entry from the routing table.

There is no way with ioctl to list all the entries in the routing table. This operation is usually performed by the netstat program when invoked with the -r flag. This program
obtains the routing table by reading the kernel's memory (/dev/kmem). As with listing the ARP cache, we will see an easier (and better) way to do this using sysctl

ARP Cache Operations

struct arpreq {
struct sockaddr arp_pa; /* protocol address */
struct sockaddr arp_ha; /* hardware address */
int arp_flags; /* flags */
};
#define ATF_INUSE 0x01 /* entry in use */
#define ATF_COM 0x02 /* completed entry (hardware addr valid) */
#define ATF_PERM 0x04 /* permanent entry */
#define ATF_PUBL 0x08 /* published entry (respond for other host) */

The third argument to ioctl must point to one of these structures. The following three requests are supported:

SIOCSARP Add a new entry to the ARP cache or modify an existing entry. arp_pa is an Internet socket address structure containing the IP address, and arp_ha is a generic socket address structure with sa_family set to AF_UNSPEC and sa_data containing the hardware address (e.g., the 6-byte Ethernet address). The two flags, ATF_PERM and ATF_PUBL, can be specified by the application. The other two flags, ATF_INUSE and ATF_COM, are set by the kernel.
SIOCDARP Delete an entry from the ARP cache. The caller specifies the Internet address for the entry to be deleted.
SIOCGARP Get an entry from the ARP cache. The caller specifies the Internet address, and the corresponding Ethernet address is returned along with the flags.

#include <net/if.h>
#include <list>
#include <string>
#include <signal.h>
#include <sys/time.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <iostream>
#include <cerrno>
#include <cstdlib>
#include <string.h>
#include <netinet/udp.h>
#include<sys/ioctl.h>
#include <netinet/in.h>
#include <net/if_arp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <net/if_arp.h>

using namespace std;

struct ifi_info
{ 
	string ifi_name;
	u_char ifi_haddr[ 8 ]; 
	int ifi_hlen;
	short ifi_mtu;

	short ifi_flags;
	short ifi_myflags;
	struct sockaddr *ifi_addr;
	struct sockaddr *ifi_brdaddr;
};

typedef list<struct ifi_info*> list_info;

char *sock_ntop_host( struct sockaddr *sa, socklen_t salen)  
{  
    static char str[ 128 ];  
    struct sockaddr_in *sin = (struct sockaddr_in *) sa;  
  
    if ( inet_ntop( AF_INET, &sin->sin_addr, str, sizeof( str ) ) == 0 )  
    { 
	    cout << "sock_ntop_host error" << endl;
	    exit( 1 ); 
    }
  
    return str;  
}

void get_ifi_info( list_info &info )
{ 
	int sockfd = socket( AF_INET, SOCK_DGRAM, 0 ); 

	int lastlen = 0;
	int len = 100 * sizeof( struct ifreq ); 
	struct ifconf ifc;
	char *buf = 0;

	while ( 1 )
	{
		buf = new char[ len ];
		ifc.ifc_len = len;
		ifc.ifc_buf = buf;

		if ( ioctl( sockfd, SIOCGIFCONF, &ifc ) < 0 )
		{
			if ( errno != EINVAL || lastlen != 0 )
			{
				cout << "ioctl error" << endl;
				exit( 1 ); 
			}
		}
		else
		{ 
			if ( ifc.ifc_len == lastlen )
			{
				break;
			}

			lastlen = ifc.ifc_len;
		}

		len += 10 * sizeof( struct ifreq ); 

		delete[] buf;
	}

	char *haddr;

	string lastname;
	for ( char *ptr = buf; ptr < buf + ifc.ifc_len; )
	{
		struct ifreq *ifr = (struct ifreq*)ptr;
		if ( ifr->ifr_addr.sa_family != AF_INET )
		{
			continue;
		}

		len = sizeof( struct sockaddr ); 
#ifdef HAVE_SOCKADDR_SA_LEN
		len = max( len, ifr->ifr_addr.sa_len ); 
#endif
		ptr += sizeof( ifr->ifr_name ) + len;

		char *cptr = strchr( ifr->ifr_name, ':' );
		if ( cptr != 0 )
		{
			*cptr = 0;
		}

		int myflags = 0;
		string str = ifr->ifr_name;
		if ( str == lastname )
		{
			myflags = 1;
		}
		lastname = str;

		struct ifreq ifrcopy = *ifr;
		ioctl( sockfd, SIOCGIFFLAGS, &ifrcopy ); 
		int flags = ifrcopy.ifr_flags;
		if ( !( flags & IFF_UP ) )
		{
			continue;
		}

		struct ifi_info *ifi = new struct ifi_info();

		ifi->ifi_flags = flags;
		ifi->ifi_myflags = myflags;
		ifi->ifi_name = ifr->ifr_name;

		ioctl( sockfd, SIOCGIFMTU, &ifrcopy ); 
		ifi->ifi_mtu = ifrcopy.ifr_mtu;
			
		struct sockaddr_in *sinptr = (struct sockaddr_in*)&ifrcopy.ifr_addr;
		ifi->ifi_addr = (struct sockaddr*)( new struct sockaddr_in() );
		memcpy( ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in) ); 

		if ( IFF_BROADCAST & flags )
		{
			ioctl( sockfd, SIOCGIFBRDADDR, &ifrcopy ); 

			sinptr = (struct sockaddr_in*)&ifrcopy.ifr_broadaddr;
			ifi->ifi_brdaddr = (struct sockaddr*)( new struct sockaddr_in() );
			memcpy( ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in) ); 
		}

		ifrcopy.ifr_hwaddr.sa_family = ARPHRD_ETHER;
		ioctl( sockfd, SIOCGIFHWADDR, &ifrcopy ); 
		ifi->ifi_hlen = 6;
		memcpy( ifi->ifi_haddr, ifrcopy.ifr_hwaddr.sa_data, 6 );

		info.push_back( ifi ); 
	}
		
	delete[] buf;
}

void free_ifi_info( list_info &info ) 
{ 
	for ( list_info::iterator it = info.begin(); it != info.end(); ++it )
	{
		struct ifi_info *ifi = *it;

		if ( 0 != ifi->ifi_addr )
		{
			delete ifi->ifi_addr; 
		}

		if ( 0 != ifi->ifi_brdaddr )
		{
			delete ifi->ifi_brdaddr; 
		}

		delete ifi; 
	}

	info.clear();
}

int main( int argc, char *argv[] ) 
{ 
	list_info info;
	get_ifi_info( info );

	for ( list_info::iterator it = info.begin(); it != info.end(); ++it )
	{
		struct ifi_info *ifi = *it;

		cout << ifi->ifi_name << " : < ";

		if ( IFF_UP & ifi->ifi_flags )
		{
			cout << "up ";
		}

		if ( IFF_BROADCAST & ifi->ifi_flags )
		{
			cout << "bcast ";
		}

		if ( IFF_MULTICAST & ifi->ifi_flags )
		{
			cout << "mcast ";
		}

		if ( IFF_LOOPBACK & ifi->ifi_flags )
		{
			cout << "loop ";
		}

		if ( IFF_POINTOPOINT & ifi->ifi_flags )
		{
			cout << "p2p ";
		}

		cout << ">" << endl;
		
		u_char *p = ifi->ifi_haddr;
		cout << "  HWaddr : ";
		for ( int i = 0; i < ifi->ifi_hlen; ++i )
		{
			cout << hex << (int) p[ i ];
			if ( i < ifi->ifi_hlen - 1 )
			{
				cout << ":";
			}
		}
		cout << endl;

		if ( 0 != ifi->ifi_mtu )
		{
			cout << "  mtu:" << ifi->ifi_mtu << endl;
		}

		struct sockaddr *sa = ifi->ifi_addr;
		if ( sa != 0 )
		{
			cout << "  IP addr:" << sock_ntop_host( sa, sizeof(*sa) ) << endl;
		}

		sa = ifi->ifi_brdaddr;
		if ( sa != 0 )
		{
			cout << " broadcast addr:" << sock_ntop_host( sa, sizeof(*sa) ) << endl;
		}
	}

	free_ifi_info( info ); 

	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值