在终端下输入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的流程也是类似的。