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的流程也是类似的。

    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/chenming321/article/details/40888459
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞