《ifconfig源码分析之与内核交互数据》
本文档的Copyleft归rosetta所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性。参考资料:《Linux设备驱动程序 第三版》,scull源码,Linux内核源码来源:http://blog.csdn.net/rosetta/article/details/7563615 ifconifg是Linux提供的一个操作网络接口的应用层程序,虽然和设备驱动编写没什么联系,但分析它的部分核心代码有助于理解应用层和内核层交互过程。 这也是对《字符设备驱动程序编写基础》最后提出的问题的一个解答。 ifconifg.c文件一千多行再加上相关公共文件大概会达到二千行,只分析其与内核交互过程,其它部分有兴趣的朋友可以自行分析。知识点:* 获取ifconfig源码方法。* ifconfig 输出结果解释。* 应用层和内核层交互过程。* ioctl的使用。* 认识/proc/net/dev。一、获取ifconifg源码包并编译。 [root@xxx net-tools-1.60]# type ifconfig ifconfig is hashed (/sbin/ifconfig) [root@xxx net-tools-1.60]# rpm -qf /sbin/ifconfig net-tools-1.60-78.el5 可知ifconfig属于net-tools源码包,下载之。net-tools源码包不仅包含ifconifg,还包含常用的arp、route、netstat等工具源码。 直接make,应该会有错误,按着错误提示修改下源码即可。二、ifconifg eth0执行结果解释 [root@ xxx]# ./ifconfig eth0 eth0 Link encap:Ethernet HWaddr 00:0C:29:9a:26:37 inet addr:192.168.95.162 Bcast:192.168.95.255 Mask:255.255.255.0 inet6 addr: fe80::21c:29ff:fe9b:2637/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:2495308 errors:0 dropped:0 overruns:0 frame:0 TX packets:2215616 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:998016881 (951.7 MiB) TX bytes:886972155 (845.8 MiB) Interrupt:18 Base address:0x2000 Link encap:Ethernet 本网卡接入的网络的类型是以太网。 HWaddr 00:0C:29:9a:26:37 本网卡的硬件地址。 inet6 addr: fe80::21c:29ff:fe9b:2637/64 Scope:Link ipv6地址。 UP 网卡状态为开启。 BROADCAST 支持广播。 RUNNING 网卡的网线被接上。 MULTICAST 支持多播。 MTU:1500 IP数据包的最大长度,带IP头。 RX表示接收数据包的情况。 TX表示发送数据包的情况。 如果网卡已经完成配置却还是无法与其它设备通信,那么从RX 和TX 的显示数据上可以简单地分析一下故障原因。在这种情况下,如果接收和传送的包的计数(packets)增加,那有可能是系统的IP地址出现了冲突;如果看到大量的错误(errors)和冲突(Collisions),那么这很有可能是网络的传输介质出了问题,例如网线不通或hub损坏。 collisions: 网络讯号碰撞的情况说明 txqueuelen: 传输缓区长度大小三、认识/proc/net/dev 这里列出了所有网络设备的其属性状态和收发包情况。ifconfig会open这个设备查找匹配信息。 [root@xxx ipsec]# cat /proc/net/dev Inter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed lo: 14920 167 0 0 0 0 0 0 14920 167 0 0 0 0 0 0 eth0:104165628 231316 5 5 0 0 0 0 27195571 185064 0 0 0 0 0 0 eth1: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 eth2: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 sit0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ipsec0: 128 2 0 0 0 0 0 0 900 6 0 0 0 0 0 0 ipsec1: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ipsec2: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ipsec3: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 sn0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 sn1: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0四、分析./ifconfig eth0 源码执行流程 前面部分是对选项的解析判断,给出函数调用过程,具体内容跳过。 //ifconfig.c main() ->if_print()//输入参数为"eth0" ->lookup_interface() ->do_if_fetch() ->if_fetch()//从内核获取网卡信息,也是和内核交互的核心 ->ife_print()//再把接收到的数据以第二步的格式打出 int if_fetch(struct interface *ife) { struct ifreq ifr; int fd; char *ifname = ife->name; strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0)//skfd为本地域套接字,SIOCGIFFLAGS为传给内核的cmd,ifr接收从内核返回的数据。 return (-1); ife->flags = ifr.ifr_flags; strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) memset(ife->hwaddr, 0, 32); else memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8); ife->type = ifr.ifr_hwaddr.sa_family; …… } 讲到这里,我觉得就讲完了,虽然没有很高深的内容,但原本在脑海中模糊的概念已经变得清晰。 再帖上一段内核有关ioctl处理的源码:int dev_ioctl(unsigned int cmd, void __user *arg){ struct ifreq ifr; int ret; char *colon; /* One special case: SIOCGIFCONF takes ifconf argument and requires shared lock, because it sleeps writing to user space. */ if (cmd == SIOCGIFCONF) { rtnl_shlock(); ret = dev_ifconf((char __user *) arg); rtnl_shunlock(); return ret; } if (cmd == SIOCGIFNAME) return dev_ifname((struct ifreq __user *)arg); if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) return -EFAULT; ifr.ifr_name[IFNAMSIZ-1] = 0; colon = strchr(ifr.ifr_name, ':'); if (colon) *colon = 0; /* * See which interface the caller is talking about. */ switch (cmd) { /* * These ioctl calls: * - can be done by all. * - atomic and do not require locking. * - return a value */ case SIOCGIFFLAGS://here case case SIOCGIFMETRIC: case SIOCGIFMTU: case SIOCGIFHWADDR: case SIOCGIFSLAVE: case SIOCGIFMAP: case SIOCGIFINDEX: case SIOCGIFTXQLEN: dev_load(ifr.ifr_name); read_lock(&dev_base_lock); ret = dev_ifsioc(&ifr, cmd);//here read_unlock(&dev_base_lock); if (!ret) { if (colon) *colon = ':'; if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) ret = -EFAULT; } return ret; …… } /* * Perform the SIOCxIFxxx calls. */ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) { int err; struct net_device *dev = __dev_get_by_name(ifr->ifr_name); if (!dev) return -ENODEV; switch (cmd) { case SIOCGIFFLAGS: /* Get interface flags */ ifr->ifr_flags = dev_get_flags(dev);//给ifr赋值 return 0; case SIOCSIFFLAGS: /* Set interface flags */ return dev_change_flags(dev, ifr->ifr_flags); …… }