五十九 struct ifreq结构体与ip,子网掩码,网关等信息

来源:互联网 发布:小微企业的税务数据 编辑:程序博客网 时间:2024/06/13 21:14

总结一下,今天学习的关于通过socket,ioctl来获得ip,netmask等信息,其中很多内容参照了很多网上的信息,我会一一列出的

我用的这个函数,就是下面这个函数,其中的有一些全局变量,很好懂,也就不多做解释了
一。下面对这个函数进行注解一下:

int get_nic_IP_Address()//获取各网卡IP地址、子网掩码
{
 struct ifreq ifreq;  //声明一个struct ifreq结构体(这个结构体中有很多重要的参数,具体可以参照第二的补充)
   int sock;
 int i;
 int tmpint;
 read_dev(); //这个函数的功能是获得网卡名字(保存在下面提到的sys_nic_ip[][]数组中)并计算网卡总数(就是下面的sys_nic_count)

 for (i=0;i<sys_nic_count;i++)
 {
     if((sock=socket(AF_INET,SOCK_STREAM,0))<0){  //建立一个套接字
         perror("socket");
         return ;
        }
     strcpy(ifreq.ifr_name,sys_nic_name[i]);   //把网卡名字复制到ifreq结构体中的name变量(感觉这个地方是必须的)
     if(ioctl(sock,SIOCGIFADDR,&ifreq)<0) {   //这里涉及ioctl函数对于网络文件的控制(下面会介绍)
       sprintf(sys_nic_ip[i],"Not set");
     } else {
       sprintf(sys_nic_ip[i],"%d.%d.%d.%d",      //把ip地址提取出来,保存(理解一下socketaddr_in和socketaddr的关系)
       (unsigned char)ifreq.ifr_addr.sa_data[2],
       (unsigned char)ifreq.ifr_addr.sa_data[3],
       (unsigned char)ifreq.ifr_addr.sa_data[4],
       (unsigned char)ifreq.ifr_addr.sa_data[5]);
     }
     if(ioctl(sock,SIOCGIFNETMASK,&ifreq)<0) {  //我的理解是这个地方用SIOCGIFNETMASK,那么ifreq中原本是存的ip地址,现在存成了子网掩码了。。
       sprintf(sys_nic_mask[i],"Not set");       //把子网掩码提取出来(但得到的只是超网的划分方式就是/xx)
     } else {
       sprintf(sys_nic_mask[i],"%d",
       Count((unsigned char)ifreq.ifr_netmask.sa_data[2])+
       Count((unsigned char)ifreq.ifr_netmask.sa_data[3])+
       Count((unsigned char)ifreq.ifr_netmask.sa_data[4])+
       Count((unsigned char)ifreq.ifr_netmask.sa_data[5]));
       
     }
 }
}
列出上面最后调用函数(Count())和一些全副变量:
char sys_nic_ip[20][20];//各网卡IP
char sys_nic_mask[20][20];//各网卡子网掩码"/xx"

int countTable[256] =
{
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};
int Count(int v)

  return countTable[v]; 
}
应该理解了吧。。。挺经典的。。。不过网上的貌似就有一个版本。。。很是气恼

二。对涉及的知识点进行补充

1.struct ifreq {
    char ifr_name[IFNAMSIZ];
     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)


2.ioctl 函数 (在网络中的作用)
关于这个网络相关的请求,就是ioctl在这里面起的作用和各个参数的作用。。。可以参照这个网页,讲解的很详细:
http://www.iteye.com/topic/309442
本例中用的2个ioctl控制函数。。上面已经解释很清楚了

3.关于socketaddr_in和socketaddr的关系,下面贴出具体的定义:
struct sockaddr_in 

short int sin_family; /* 地址族 */ 
unsigned short int sin_port; /* 端口号 */ 
struct in_addr sin_addr; /* IP地址 */ 
unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */ 
};

struct sockaddr 

unsigned short sa_family; /* 地址族, AF_xxx */ 
char sa_data[14]; /* 14 字节的协议地址 */ 
};

比较一下,会发现长度一样,所以这2个可以通用的,不过要进行类型转换,比较一下就得出了为什么上面程序中可以用:
(unsigned char)ifreq.ifr_addr.sa_data[2],这种形式了,还是解释一下吧:这个ifr_addr是一个struct sockaddr结构体。它其中的sa_date[2]是不是照着上面sockaddr_in中的sin_add(也就是ip地址呢),该明白了吧。。。。

总结:通过这个函数,可以很好的理解怎么得到ip和子网掩码的过程。。。。


写一个检测网线是否被拔出的守护进程(嵌入式设备上)

/*

        博主注明:

               编译环境: Ubuntu 10.4    编译器:arm-linux-gcc

               硬件环境:i.mx28  嵌入式linux版本 2.6.15

               介绍:

                             参考ethtool工具源代码,发现在对网卡的ioctl操作中,可以读出网卡的状态

*/


[plain] view plaincopy
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <stdlib.h>  
  4. #include <signal.h>  
  5. #include <sys/file.h>  
  6. #include <netinet/in.h>  
  7. #include <linux/if.h>  
  8. #include <string.h>  
  9. #include <sys/types.h>  
  10. #include <arpa/inet.h>  
  11. #include <sys/ioctl.h>  
  12.   
  13.    
  14.   
  15. //由于嵌入式linux貌似没有对ethtool_value这个结*构体的定义,但在非嵌入式的版本里是有的,所以直接抄过来= =...  
  16. struct ethtool_value {  
  17.     int cmd;  
  18.     int data;  
  19. };  
  20.   
  21.    
  22.   
  23. //以下两个宏和ioctl有关,从网卡里获取网线的状态的关键就在这里  
  24. #define ETHTOOL_GLINK        0x0000000a  
  25. #define SIOCETHTOOL            0x8946  
  26. #define UNCONNECT                0  
  27. #define CONNECT                    1  
  28.   
  29. int main(int argc, char **argv)  
  30. {  
  31.     int i, fd, fd2, fdtablesize, sock, last, ret;  
  32.     pid_t pid;  
  33.   
  34.   
  35.     struct sigaction sa;  
  36.     struct ifreq ifr;  
  37.     struct ethtool_value edata;  
  38.   
  39.   
  40.     edata.cmd = ETHTOOL_GLINK;  
  41.     edata.data = 0;  
  42.      
  43. //以下是产生一个守*护进程的方法,具体注解就不再赘述,之前的博文里有详解  
  44.     umask(0);  
  45.     sa.sa_handler = SIG_IGN;  
  46.     sigemptyset(&sa.sa_mask);  
  47.     sa.sa_flags = 0;  
  48.   
  49.   
  50.     //shutdown some signal  
  51.     if(sigaction(SIGTTOU, &sa, NULL) < 0)  
  52.     {  
  53.         printf("can't ignore SIGTTOU\n");  
  54.         return -1;  
  55.     }  
  56.      
  57.     if(sigaction(SIGTTIN, &sa, NULL) < 0)  
  58.     {  
  59.         printf("can't ignore SIGTTIN\n");  
  60.         return -1;  
  61.     }  
  62.      
  63.     if(sigaction(SIGTSTP, &sa, NULL) < 0)  
  64.     {  
  65.         printf("can't ignore SIGTSTP\n");  
  66.         return -1;  
  67.     }  
  68.      
  69.     if(sigaction(SIGHUP, &sa, NULL) < 0)  
  70.     {  
  71.         printf("can't ignore SIGHUP\n");  
  72.         return -1;  
  73.     }  
  74.      
  75.     //terminate the father thread first  
  76.     if(fork() != 0)  
  77.     {  
  78.         exit(1);  
  79.     }  
  80.     if(setsid() < 0)  
  81.     {  
  82.         exit(1);  
  83.     }  
  84.     //terminate the father thread second  
  85.     if(fork() != 0)  
  86.     {  
  87.         exit(1);  
  88.     }  
  89.     //change work dir to root  
  90.     if(chdir("/") == -1)  
  91.     {  
  92.         exit(1);  
  93.     }  
  94.     //shutdown some fds  
  95.     for(fd = 0, fdtablesize = getdtablesize(); fd < fdtablesize; fd++)  
  96.     {  
  97.         close(fd);  
  98.     }  
  99.     if(sigaction(SIGCHLD, &sa, NULL))  
  100.     {  
  101.         printf("can't ignore SIGCHLD\n");  
  102.         return -1;  
  103.     }  
  104.   
  105.     //access a socket  
  106.     sock = socket(AF_INET, SOCK_DGRAM, 0);  
  107.     if(sock == -1)  
  108.     {  
  109.         return -1;  
  110.     }  
  111.   
  112. //这里是将要查询的端口名拷贝到指定位置  
  113.     strcpy(ifr.ifr_name, "eth0");  
  114.     ifr.ifr_data = (char *)&edata;  
  115.   
  116. //获取状态  
  117.     ioctl(sock, SIOCETHTOOL, &ifr);     
  118.     last = edata.data;  
  119.   
  120. //不断的读取网卡的状态  
  121.     while(1)  
  122.     {  
  123.   
  124.  //偶尔也让它休息一下^_^  
  125.   
  126.         sleep(1);  
  127.         ioctl(sock, SIOCETHTOOL, &ifr);  
  128.   
  129.   
  130. //如果状态没有改变,就跳过,直接执行下一次查询  
  131.         if(edata.data == last)  
  132.         {  
  133.             continue;  
  134.         }  
  135.         else  
  136.         {  
  137.             if(last == UNCONNECT)   //如果网线被插上了  
  138.             {  
  139.                 //打开一个子进程,在里面调用一个脚本  
  140.                 if(fork() == 0)  
  141.                 {  
  142.                 //  pid = getpid();  
  143.                     ret = execl("/data/work/ma_to_mt.sh", "master", NULL);  
  144.                     if(ret < 0)  
  145.                     {  
  146.                         exit(1);  
  147.                     }  
  148.          //          return 0;  //这里没有必要使用return  因为execl执行了就不会返回了       
  149.                 }  
  150.                 last = CONNECT;  //把状态改为已连接  
  151.             }  
  152.             else if(last == CONNECT)   //如果断开了网线  
  153.             {  
  154.                 if(fork() == 0)         //开一个子进程,运行另一个脚本  
  155.                 {  
  156.          //           pid = getpid();  
  157.                     ret = execl("/data/work/mt_to_ma.sh", "managed", NULL);  
  158.                     if(ret < 0)  
  159.                     {  
  160.                         exit(1);  
  161.                     }  
  162.                 }  
  163.                 last = UNCONNECT;    //状态改为已断开  
  164.             }  
  165.              
  166.         }     
  167.         waitpid(-1, NULL ,0);     //这个回收子进程的动作貌似也是没必要的= =....  
  168.     }  
  169.     return 0;  
  170. }  

一个应用的例子 KAER3518设备(检查有线网卡是否连接及状态

void net_CheckEth0Link(void)
{
    int sock;
struct ifreq ifr;


    sock = socket(AF_INET,SOCK_DGRAM,0);
if (sock < 0)
{
perror("net_CheckEth0Link:创建sock失败");
return;
}
    strcpy(ifr.ifr_name,"eth0");
    if (ioctl(sock,SIOCGIFFLAGS,(char *)&ifr) >= 0)
    {
    if (ifr.ifr_flags & IFF_UP)// 代表了网络装置是否正常启用
        {
            struct ethtool_value edata; //  ????????????????????
            edata.cmd    = ETHTOOL_GLINK; //  功能是 插拔网线 能立即检测到
            ifr.ifr_data = (char *)&edata;
            if (!(ioctl(sock, SIOCETHTOOL, &ifr)<0))
            {
                if (edata.data == 0)
                {
                printf("注意: 网线已经被拔出\n");
                g_st_netStatus.lan.sDevice = PHY_STATUS_UNLINK;
                }
                else if (edata.data > 0)
                {
                g_st_netStatus.lan.sDevice = PHY_STATUS_LINK100M;
                }
                else
                {
                    g_st_netStatus.lan.sDevice = PHY_STATUS_IOCTRL_ILLEGAL;
                    printf("ioctl 网卡状态非法值 %d\n",edata.data);
                }
            }
            else
            {
                g_st_netStatus.lan.sDevice = PHY_STATUS_IOCTRL_ERR;
                perror("SIOCETHTOOL ioctl 执行失败\n");
            }
        }
        else
        {
        g_st_netStatus.lan.sDevice = PHY_STATUS_NOCARD;
        printf("ifr.ifr_flags %08x IFF_UP %08x\n",ifr.ifr_flags,IFF_UP);
        }
    }
    else
    {
    g_st_netStatus.lan.sDevice = PHY_STATUS_IOCTRL_ERR;
perror("SIOCGIFFLAGS ioctl 执行失败\n");
    }


    close(sock);
}
0 0