50.Linux-分析ifconfig到內核的調用過程,實現內核啟機自動設MAC地址(原)


  • 內核版本: Linux version 3.10.14

 

1.由於每次開發板開機的網卡eth0的物理地址都是隨機的.

然后在網上找到可以通過命令行實現設置mac物理地址:

ifconfig eth0 down
ifconfig eth0 hw ether 1234567890ab
ifconfig eth0 up
  • 然后帶着好奇,想看看命令行ifconfig是如何與內核交互的,想試試如何直接通過內核自動設置MAC.

 

2.分析介紹

因為ifconfig是命令,代碼位於busybox,不過我們在內核的documentation目錄下找到了ifconfig介紹,代碼介紹文件位於:

  • documentation\networking\Ifenslave.c

2.1 如下圖所示,對應ifconfig eth0 down和ifconfig eth0 up的函數就是:

 

比如,當我們敲ifconfig eth0 down時,實則就是調用:

set_if_down("eth0", master_flags.ifr_flags);

該文件除了上圖外,還有以下常用函數:

set_if_addr();           //設置地址(包括IP,掩碼,廣播,目的地)
set_master_hwaddr();     //設置mac物理地址
  • 接下來我們以eth0為例,來跟蹤ifconfig up/downifconfig eth0 hw ether如何調用內核的

 

3.分析set_if_up()函數

3.1 分析set_if_up()

set_if_up()函數將會調用set_if_flags("eth0", flags | IFF_UP), 向添加ifname(eth0) 開啟標志位

    

3.2 分析set_if_up()->set_if_flags("eth0", flags | IFF_UP)

該函數如下所示:

static int set_if_flags(char *ifname, short flags)
{
         struct ifreq ifr;
         int res = 0;
         ifr.ifr_flags = flags;                                            
         strncpy(ifr.ifr_name, ifname, IFNAMSIZ);    //ifr.ifr_name="eth0"

         res = ioctl(skfd, SIOCSIFFLAGS, &ifr);    //通過ioctl()向內核socket傳遞命令SIOCSIFFLAGS和ifr變量
         if (res < 0) {
                  saved_errno = errno;
                  v_print("Interface '%s': Error: SIOCSIFFLAGS failed: %s\n",
                          ifname, strerror(saved_errno));
         } else {
                  v_print("Interface '%s': flags set to %04X.\n", ifname, flags);
         }
         return res;
}

 

3.3 尋找SIOCSIFFLAGS宏,看看內核那里在實現它

找到位於net\core\Dev_ioctl.c的dev_ioctl()函數

該函數重要部分代碼如下:

int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
{
         struct ifreq ifr;
         int ret;
         char *colon;
         //… …

         switch (cmd) {
         //… …

         case SIOCSIFFLAGS:   //設置標志,比如ifconfig  up/down
         case SIOCSIFMETRIC:    
         case SIOCSIFMTU:     //設置MUT長度
         case SIOCSIFHWADDR:  //設置mac物理地址
         //… …

         dev_load(net, ifr.ifr_name);           //通過ifr.ifr_name(eth0)名字來加載網卡
         rtnl_lock();                           //對net_device進行加鎖,避免與運行沖突
         ret = dev_ifsioc(net, &ifr, cmd);      //最終調用該函數

rtnl_unlock();
         return ret;
         //… …
}

從上面可以看出,我們設置mac物理地址時的流程也會運行到這里,最終他們都會調用dev_ifsioc(net, &ifr, cmd)函數

 

4. 后面的就很簡單了,最終ifconfig eth0 up調用內核過程為:

set_if_up()->

  set_if_flags("eth0", flags | IFF_UP)->

   dev_ifsioc(net, &ifr, cmd)->

      dev_change_flags(dev, ifr->ifr_flags)-> 

           __dev_change_flags(dev, flags);

4.1然后在__dev_change_flags(dev, flags)函數中,通過判斷flag的IFF_UP位上的值是否相反,來實現是調用__dev_close()還是__dev_open()來開關eth0

如下圖所示:

 

4.2然后__dev_open()則將會調用網卡驅動的net_device_ops結構體下的成員函數實現打開

__dev_open(dev):
   dev->netdev_ops->ndo_validate_addr(dev); //測試dev->dev_addr(hw addr)是否有效,一般都是調用eth_validate_addr()函數,需要注意hw_addr[0]的最低位不能為1
   dev->netdev_ops->ndo_open(dev);          //調用open()函數實現ifconfig up

4.3同樣__dev_close()會調用下面的成員函數實現關閉:

dev->netdev_ops->ndo_stop(dev);                //調用stop ()函數實現ifconfig down

4.4尋找net_device_ops結構體的成員函數位於哪里

上面講的dev 變量是struct net_device類型,而struct net_device在內核中表示我們的一個網卡驅動設備,注冊該變量的文件都處於內核drivers/net目錄下,通過register_netdev()      內核函數來注冊.

我們以我們板卡的dm9000網卡為例,該文件位於drivers/net/Ethernet/davicom/dm9000.c,然后便可以找到它的ndo_open ():

 

 

5.而對於ifconfig eth0 hw ether 設置網卡流程如下所示:

set_master_hwaddr(master_ifname,&(slave_hwaddr.ifr_hwaddr))->

  ioctl (skfd, SIOCSIFHWADDR, &ifr) ->                  

       dev_ifsioc(net, &ifr, cmd)->

            dev_set_mac_address(dev, &ifr->ifr_hwaddr) ->             //設置網卡MAC地址

                  dev->netdev_ops->ndo_set_mac_address(dev, &ifr->ifr_hwaddr);  
                //最終調用net_device的ops成員函數實現設置

 

6.實現內核開機自動設置固定MAC地址

流程分析完后,接下來我們便來實現它.

6.1以我們板卡的dm9000網卡為例

我們找到register_netdev()位置,位於drivers/net/Ethernet/davicom/dm9000.c的dm9000_probe函數里:

 

6.2 然后在register_netdev()函數下面添加代碼:

struct       sockaddr  hwaddr;                   //用來存儲MAC地址的結構體

rtnl_lock();
ret =dev_close(jz_ndev);                          //首先需要關閉網卡,以防萬一
rtnl_unlock();

hwaddr.sa_family = ndev->type; 

hwaddr.sa_data[0]=0x12;                      //注意,data[0]最低位不能為1,也就是首位不能為奇數
hwaddr.sa_data[1]=0x34;
hwaddr.sa_data[2]=0x56;
hwaddr.sa_data[3]=0x78;
hwaddr.sa_data[4]=0x90;
hwaddr.sa_data[5]=0xab;              

rtnl_lock();
ret = dev_set_mac_address(jz_ndev,&hwaddr);  //調用我們分析到的函數,來設置mac地址
rtnl_unlock();

6.3 編譯-試驗

 啟動后輸入ifconfig,即可看到內核已經幫我設置好了:

 

 

  • 總結:  其實實現的代碼很簡單,但是需要去分析才能把東西消化為自己的.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM