专栏首页Linux驱动Linux-分析ifconfig到内核的调用过程,实现内核启机自动设MAC地址(原)

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,即可看到内核已经帮我设置好了:

  • 总结: 其实实现的代码很简单,但是需要去分析才能把东西消化为自己的.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Liunx-常用命令的总结(5)

    cd     ../dir    上一节目录下dir目录 cd -   返回上次目录 ifconfig     查看IP地址 sudo   ifconfig ...

    张诺谦
  • 18.QT-QPlainEdit 信号与槽

    张诺谦
  • 20.Linux-USB鼠标驱动

    在上一章分析完USB总线驱动程序后, 接下来开始写一个USB驱动: 本节目的: 将USB鼠标的左键当作L按键,将USB鼠标的右键当作S按键,中键当作回车按键 参...

    张诺谦
  • Java单体应用 - 架构模式 - 03.设计模式-24.模板模式

    原文地址:http://www.work100.net/training/monolithic-architecture-design-patterns-tem...

    光束云
  • 如何扩大C盘空间,转帖至百度空间

    迁移临时文件夹,让系统效能全面提升 Windows XP系统默认将“我的文档”、Windows临时文件夹、虚拟内存、IE临时文件夹和程序安装目录等经常使用的文件...

    Dabelv
  • L016使用/dev/random生成随机数

    很多库例程产生的“随机”数是准备用于仿真、游戏等等;它们在被用于密钥生成一类的安全函数时是不够随机的。其问题在于这些库例程使用的算法的未来值可以被攻击者轻易地推...

    上善若水.夏
  • MD04详细说明(二)

    上篇介绍了MD04的MRP元素,本篇主要介绍MD04中的日期、再计划日期及MRP例外消息。

    老铁一起学
  • 【星球知识卡片】深度学习图像降噪有哪些关键技术点,如何学习

    图像去噪模型的输出是无噪声的图像,与输入图像大小相同,所以可以使用图像分割一类的模型,即经典的基于跳层连接的卷积与反卷积对称结构,优化目标为逐个像素的欧式距离损...

    用户1508658
  • 「mysql优化专题」优化之路高级进阶——表的设计及优化(6)

    数据库范式是确保数据库结构合理,满足各种查询需要、避免数据库操作异常的数据库设计方式。满足范式要求的表,称为规范化表,范式产生于20世纪70年代初,一般表设计满...

    java进阶架构师
  • Linux系统下Tomcat8启动速度很慢的解决方法

    最近在工作中遇到一个问题,在Linux下Tomcat 8启动很慢,且日志上无任何错误,在日志中查看到如下信息:

    砸漏

扫码关注云+社区

领取腾讯云代金券