struct in_addr ,struct sockadd,ifreq,eth0,ioctl

来源:互联网 发布:知乎 美国epic公司 编辑:程序博客网 时间:2024/05/20 01:47

本文以获取或设置机子IP为例分析struct in_addr ,struct sockadd,ifreq,eth0,ioctl

所有讲述均在Linux环境下完成

程序会用到以下结构体(结构体都有说明)

struct in_addr      //用来保存以十六进制表示的IP地址
struct in_addr { 

  unsigned long s_addr;

};

typedef struct in_addr {
union {
           struct{unsigned char s_b1,s_b2,s_b3,s_b4;} S_un_b;
           struct{unsigned short s_w1,s_w2;} S_un_w;
           unsigned long S_addr;
          } S_un;
} IN_ADDR;

struct sockaddr结构类型是用来保存socket信息的:

struct sockaddr {   
              unsigned short    sa_family;                    
              char sa_data[14];                                           
               };
             sa_family一般为AF_INET,代表Internet(TCP/IP)地址族;
              sa_data则包含该socket的IP地址和端口号。
另外更有一种结构类型:   
struct sockaddr_in {    
              short int sin_family;                    
            unsigned short int sin_port;                 
              struct in_addr sin_addr;                    
              unsigned char sin_zero[8];                 

                          };  

这个结构更方便使用。sin_zero用来将sockaddr_in结构填充到和struct sockaddr同样的长度,能用bzero()或memset()函数将其置为零。指向sockaddr_in的指针和指向sockaddr的指针能相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你能在函数调用的时候将一个指向sockaddr_in的指针转换为指向sockaddr的指针;或相反。

 

在 /usr/include/linux/if.h里可以看到ifreq的定义

struct ifreq  结构类型用来获得指定接口的信息,接口由ifrn_name确定

struct ifreq

{

#define IFHWADDRLEN     6

        union

        {

                char    ifrn_name[IFNAMSIZ];            

        } ifr_ifrn;

 

        union {

                struct  sockaddr ifru_addr;

                struct  sockaddr ifru_dstaddr;

                struct  sockaddr ifru_broadaddr;

                struct  sockaddr ifru_netmask;

                struct  sockaddr ifru_hwaddr;

                short   ifru_flags;

                int     ifru_ivalue;

                int     ifru_mtu;

                struct  ifmap ifru_map;

                char    ifru_slave[IFNAMSIZ];  

                char    ifru_newname[IFNAMSIZ];

                void *  ifru_data;

                struct  if_settings ifru_settings;

        } ifr_ifru;

};

 

#define ifr_name        ifr_ifrn.ifrn_name      

#define ifr_hwaddr      ifr_ifru.ifru_hwaddr   

#define ifr_addr        ifr_ifru.ifru_addr      

#define ifr_dstaddr     ifr_ifru.ifru_dstaddr  

#define ifr_broadaddr   ifr_ifru.ifru_broadaddr

#define fr_netmask     ifr_ifru.ifru_netmask  

#define ifr_flags       ifr_ifru.ifru_flags    

#define ifr_metric      ifr_ifru.ifru_ivalue   

#define ifr_mtu         ifr_ifru.ifru_mtu      

#define ifr_map         ifr_ifru.ifru_map      

#define ifr_slave       ifr_ifru.ifru_slave    

#define ifr_data        ifr_ifru.ifru_data     

#define ifr_ifindex     ifr_ifru.ifru_ivalue   

#define ifr_bandwidth   ifr_ifru.ifru_ivalue   

#define ifr_qlen        ifr_ifru.ifru_ivalue   

#define ifr_newname     ifr_ifru.ifru_newname  

#define ifr_settings    ifr_ifru.ifru_settings  

 

/usr/include/linux/sockio.h里可以查看对应操作的命令,如下列举部分

 

#define SIOCINQ         FIONREAD

#define SIOCOUTQ        TIOCOUTQ

#define SIOCADDRT       0x890B         

#define SIOCDELRT       0x890C         

#define SIOCRTMSG       0x890D          

#define SIOCGIFNAME     0x8910          /* get iface name               */

#define SIOCSIFLINK     0x8911         

#define SIOCGIFCONF     0x8912          

#define SIOCGIFFLAGS    0x8913         

#define SIOCSIFFLAGS    0x8914          

#define SIOCGIFADDR     0x8915          

#define SIOCSIFADDR     0x8916         

#define SIOCGIFDSTADDR  0x8917         

#define SIOCSIFDSTADDR  0x8918         

#define SIOCGIFBRDADDR  0x8919         

#define SIOCSIFBRDADDR  0x891a         

#define SIOCGIFNETMASK  0x891b          

#define SIOCSIFNETMASK  0x891c         

 

以上是要用到的数据类型以及命令等,

示例程序如下:

#include <errno.h>

#include <stdio.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/ioctl.h>

#include <linux/if.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <net/if_arp.h>

获得IP:

void main(void)

{

       char ifname[5] = "eth0";              //指定设备名为“eth0”,即指定对网卡进行操作

       struct ifreq ifr;

       int skfd;

       struct sockaddr_in *saddr;

 

       if ( (skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {

              return -1;

       }

 

       strncpy(ifr.ifr_name, ifname, IFNAMSIZ);          //此步骤不能省略,后面有详细讲述

       if (ioctl(skfd, SIOCGIFADDR, &ifr) < 0) {

              close(skfd);

              return -1;

       }

       close(skfd);

 

       saddr = (struct sockaddr_in *) &ifr.ifr_addr;

       printf("%s\n",inet_ntoa(saddr->sin_addr));

}

 

设置 IP

static struct sockaddr_in  sa = {

       sin_family     PF_INET,

       sin_port 0

};

void  main(void)

{

       char *ifname = "eth0"

       int skfd;

       struct ifreq ifr;

       struct in_addr ip;

 

       ip.s_addr = 0x6401a8c0;

 

       if ( (skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {

              printf("socket error");

              return -1;

       }

       sa.sin_addr.s_addr = ip.s_addr;

       strncpy(ifr.ifr_name, ifname, IFNAMSIZ);

       memcpy((char *) &ifr.ifr_addr, (char *) &sa, sizeof(struct sockaddr));

       if (ioctl(skfd, SIOCSIFADDR, &ifr) < 0) {

              close(skfd);

              printf("ioctl failed.\n");

              return -1;

       }

       close(skfd);

       return 0;

}

此处ioctl函数是在驱动程序里的,ioctl可以向内核传递参数

ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。可以对设备进行一些操作,它的调用如下:

int ioctl(int fd, ind cmd, …);

其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设

备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。

第一个参数fd是一个文件描述符,我们这里是建立的一个套接字描述符

第二个参数,是在sockios.h中定义的一个32位描述符,我们也可以在这里添加新的类型,用来扩展实际需求。

第三个参数,是一个指针,用来指向某些我们实际应用中的参数。

ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支

持,用户就可以在用户程序中使用ioctl函数控制设备的I/O通道。

 

一般的说,,用户空间的IOCTL系统调用如下所示:

 ioctl(int fd, int command, (char *) argstruct)

因为这个调用拥有与网络相关的代码,所以文件描述符号fd就是socket()系统调用所返回的,而command参数可以是/usr/include/linux/sockios.h头文件中的任何一个,以上有列举及其控制的行为,这些个命令根据它可以解决的问题所涉及的方面被分为多种的类型.

比如:

  改变路由表(SIOCADDRT, SIOCDELRT)

  读取或更新ARP/RARP缓存(SIOCDARP, SIOCSRARP)

    一般的和网络有关的函数(SIOCGIFNAME, SIOCSIFADDR等等)

网络接口相关的ioctl命令最具有代表性的特征为都是以S或G开头,其实就是设置(SET)或得到(GET)数据, getifinfo.c程序用这些命令去读取IP地址信息,硬件地址信息,广播地址信息,和与网络接口相关的标志.

对于这些ioctl,第三个参数是一个IFREQ结构体,这个结构体被定义在/usr/include/linux/if.h头文件中,以上有说明,根据常规约定,一个用户程序调用一个特定的ioctl命令如下: ioctl(sockid, SIOCDEVPRIVATE, (char *) &ifr)这里ifr是一个ifreq结构体变量,它用一个和这个设备联系的接口名称填充ifr的ifr NAME域,比如,前述的网卡接口名称为eth0(系统有一块网卡,在网卡在程序中交互为“eth0”,依次,eth1,eth2,…,需要操作第n块网卡,就指定ethn)。

程序实现为

       strncpy(ifr.ifr_name, ifname, IFNAMSIZ);  

ifname是字符串指针,即 char* ifname = "eth0"

IFNAMSIZ是系统特定的,在/usr/include/linux/if.h中 #define IFNAMSIZ        16

本文有所借鉴,希望大家相互探讨!

0 0
原创粉丝点击