Android netcfg

在终端下输入adb shell,进入android的终端中,输入netcfg 会得到以下结果:

root@ardbeg:/ # netcfg  
ip6tnl0  DOWN                                   0.0.0.0/0   0x00000080 00:00:00:00:00:00  
wlan0    DOWN                                   0.0.0.0/0   0x00001002 00:90:4c:11:22:33  
rmnetctl DOWN                                   0.0.0.0/0   0x00000080 00:00:00:00:00:00  
lo       UP                                   127.0.0.1/8   0x00000049 00:00:00:00:00:00  
sit0     DOWN                                   0.0.0.0/0   0x00000080 00:00:00:00:00:00  
eth0     UP                                     0.0.0.0/0   0x00001003 92:a5:82:29:53:e4  

    其中第一项为网卡接口类型,第二项为打开关闭状态,第三项为分配的ip地址,第四项为接口标志,第五项为硬件地址。netcfg的源码在系统下位置为:platform/systom/core/netcfg/netcfg.c 

        首先看下它的主函数:

int main(int argc, char **argv)  
{  
    char *iname;  
    int n;  
      
    if(ifc_init()) {  
        die("Cannot perform requested operation");  
    }  
  
    if(argc == 1) {  
        int result = dump_interfaces();  
        ifc_close();  
        return result;  
    }  
  
    if(argc < 3) usage();  
  
    iname = argv[1];  
    if(strlen(iname) > 16) usage();  
    argc -= 2;  
    argv += 2;  
    while(argc > 0) {  
        for(n = 0; CMDS[n].name; n++){  
            if(!strcmp(argv[0], CMDS[n].name)) {  
                char *cmdname = argv[0];  
                int nargs = CMDS[n].nargs;  
                  
                argv[0] = iname;  
                if(argc < nargs) {  
                    fprintf(stderr, "not enough arguments for '%s'\n", cmdname);  
                    ifc_close();  
                    exit(1);  
                }  
                if(call_func(CMDS[n].func, nargs, argv)) {  
                    fprintf(stderr, "action '%s' failed (%s)\n", cmdname, strerror(errno));  
                    ifc_close();  
                    exit(1);  
                }  
                argc -= nargs;  
                argv += nargs;  
                goto done;  
            }  
        }  
        fprintf(stderr,"no such action '%s'\n", argv[0]);  
        usage();  
    done:  
        ;  
    }  
    ifc_close();  
    return 0;  
} 

开始时,是俩个变量的申明,iname表示接口名,n表示接口的数目,接下来if 判断会检测ifc(interface config)是否初始化,如果没有初始化,则会报出无法处理请求的错误。ifc_init()函数位于: platform/system/core/libnetutils/ifc_utils.c 下

int ifc_init(void)  
{  
    int ret;  
    if (ifc_ctl_sock == -1) {  
        ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0);  
        if (ifc_ctl_sock < 0) {  
            printerr("socket() failed: %s\n", strerror(errno));  
        }  
    }  
  
    ret = ifc_ctl_sock < 0 ? -1 : 0;  
    if (DBG) printerr("ifc_init_returning %d", ret);  
    return ret;  
}  

该函数的主要功能是,打开一个socket通信,并将返回的fd 赋给全局变量 ifc_ctl_sock(目的后面再提),成功打开的话返回-1。该libnetutils目录 在编译的时候会生成一个libnetutils.so的动态库,被netcfg所调用,这里面的一些函数netcfg还会调用。接下来的 if 判断是netcfg后面的跟的参数的判断,如果没有跟任何参数则调用 dump_interfaces() 

int dump_interfaces(void)  
{  
    DIR *d;  
    struct dirent *de;  
  
    d = opendir("/sys/class/net");  
    if(d == 0) return -1;  
  
    while((de = readdir(d))) {  
        if(de->d_name[0] == '.') continue;  
        dump_interface(de->d_name);  
    }  
    closedir(d);  
    return 0;  
}  

该函数的功能:首先打开系统下的/sys/class/net 目录,可以在终端下输入:ls /sys/class/net 查看该目录下的情况

root@ardbeg:/ # ls sys/class/net                                                 
eth0  
ip6tnl0  
lo  
rmnetctl  
sit0  
wlan0  

可以看出该目录下显示的是网卡接口名,回到 dump_interfaces()  函数中接下来进入一个while循环,主要功能是获取该目录下的每一个文件名,并传给函数dump_interface()

int dump_interface(const char *name)  
{  
    unsigned addr, flags;                                                          // 申明IP地址变量和接口标志变量  
    unsigned char hwbuf[ETH_ALEN];                                                 // 申明MAC地址变量  
    int prefixLength;                                                              // 申明网络掩码变量  
  
    if(ifc_get_info(name, &addr, &prefixLength, &flags)) {                         // 根据接口名获取之前申明变量的信息(IP地址,掩码长度,接口标志)  
        return 0;  
    }  
  
    printf("%-8s %s  ", name, flags & 1 ? "UP  " : "DOWN");                        //*** 以指定格式  
    printf("%40s", ipaddr(addr));                                                  //*** 打印  
    printf("/%-4d", prefixLength);                                                 //*** 变量  
    printf("0x%08x ", flags);                                                      //*** 值  
    if (!ifc_get_hwaddr(name, hwbuf)) {                                            // 根据接口名获取MAC地址  
        int i;  
        for(i=0; i < (ETH_ALEN-1); i++)  
            printf("%02x:", hwbuf[i]);                                             // 打印MAC地址的值  
        printf("%02x\n", hwbuf[i]);  
    } else {  
        printf("\n");  
    }  
    return 0;                                                                                   // 返回  
} 

该函数的中主要就是 ifc_get_info 和 ifc_get_hwaddr 俩个函数,分别获取IP地址信息和Mac地址信息,函数的实现位于之前提到的的 platform/system/core/libnetutils/ifc_utils.c 下。

int ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength, unsigned *flags)  
{  
    struct ifreq ifr;<span style="white-space:pre">                                       </span>  
    ifc_init_ifr(name, &ifr);  
  
    if (addr != NULL) {  
        if(ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr) < 0) {  
            *addr = 0;  
        } else {  
            *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;  
        }     
    }     
      
    if (prefixLength != NULL) {  
        if(ioctl(ifc_ctl_sock, SIOCGIFNETMASK, &ifr) < 0) {  
            *prefixLength = 0;   
        } else {  
            *prefixLength = ipv4NetmaskToPrefixLength(  
                    ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr);  
        }             
    }     
      
    if (flags != NULL) {  
        if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) {  
            *flags = 0;  
        } else {  
            *flags = ifr.ifr_flags;  
        }     
    }     
      
    return 0;  
}   

  

通过 ioctl 与内核进行通信 ,处理函数位于 kernel/net/ipv4/devinet.c 中,

int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)  
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))  
    goto out;  
ifr.ifr_name[IFNAMSIZ - 1] = 0;  
  
/* save original address for comparison */  
memcpy(&sin_orig, sin, sizeof(*sin));  
  
colon = strchr(ifr.ifr_name, ':');  
if (colon)  
    *colon = 0;  
  
dev_load(net, ifr.ifr_name);  

保存从用户空间传进来相关请求信息,并保存在ifr(ifreq)中

dev = __dev_get_by_name(net, ifr.ifr_name);  
if (!dev)  
    goto done;  
  
if (colon)  
    *colon = ':';  
  
in_dev = __in_dev_get_rtnl(dev);  
if (in_dev) {  
    if (tryaddrmatch) {  
        /* Matthias Andree */  
        /* compare label and address (4.4BSD style) */  
        /* note: we only do this for a limited set of ioctls 
           and only if the original address family was AF_INET. 
           This is checked above. */  
        for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;  
             ifap = &ifa->ifa_next) {  
            if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&  
                sin_orig.sin_addr.s_addr ==  
                        ifa->ifa_local) {  
                break; /* found */  
            }  
        }  
    }  
    /* we didn't get a match, maybe the application is 
       4.3BSD-style and passed in junk so we fall back to 
       comparing just the label */  
    if (!ifa) {  
        for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;  
             ifap = &ifa->ifa_next)  
            if (!strcmp(ifr.ifr_name, ifa->ifa_label))  
                break;  
    }  
}  

这段代码,主要是获取保存请求的设备对象节点的结构体,譬如找到了以太网的结构体信息(具体怎么获取的暂时不分析了,以后有时间在分析把),接下来则通过以下switch ,处理相关的请求信息

switch (cmd) {  
case SIOCGIFADDR:   /* Get interface address */  
    sin->sin_addr.s_addr = ifa->ifa_local;  
    goto rarok;  
  
case SIOCGIFBRDADDR:    /* Get the broadcast address */  
    sin->sin_addr.s_addr = ifa->ifa_broadcast;  
    goto rarok;  
  
case SIOCGIFDSTADDR:    /* Get the destination address */  
    sin->sin_addr.s_addr = ifa->ifa_address;  
    goto rarok;  
  
case SIOCGIFNETMASK:    /* Get the netmask for the interface */  
    sin->sin_addr.s_addr = ifa->ifa_mask;  
    goto rarok;  
  
case SIOCSIFFLAGS:  
    if (colon) {  
        ret = -EADDRNOTAVAIL;  
        if (!ifa)  
            break;  
        ret = 0;  
        if (!(ifr.ifr_flags & IFF_UP))  
            inet_del_ifa(in_dev, ifap, 1);  
        break;  
    }  
    ret = dev_change_flags(dev, ifr.ifr_flags);  
    break;  
  
case SIOCSIFADDR:   /* Set interface address (and family) */ 

所有可以看出,返回的信息 是通过 ifa->ifa_local 赋值的,最终会通过以下返回用户空间

ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;  

以上是获取Ip地址的流程,而设置IP的流程也是类似的。

相关文章
相关标签/搜索