1. 程式人生 > >ifconfig源碼分析之與內核交互數據

ifconfig源碼分析之與內核交互數據

ash mtu mic user p地址 數據 知識 chang 設備驅動

《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);
……
}

ifconfig源碼分析之與內核交互數據