50.Linux |
您所在的位置:网站首页 › eth内核评测 › 50.Linux |
内核版本: 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.c2.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/down和ifconfig 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 up4.3同样__dev_close()会调用下面的成员函数实现关闭: dev->netdev_ops->ndo_stop(dev); //调用stop ()函数实现ifconfig down4.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,即可看到内核已经帮我设置好了:
总结: 其实实现的代码很简单,但是需要去分析才能把东西消化为自己的.
人间有真情,人间有真爱,qt交流群760631646(大量文章资源)、ffmpeg交流群929155430(正在完善资源中) ![]() 如果您喜欢这里,感觉对你有帮助,并且有多余的软妹币的话,不妨投个食吧,赞赏的时候,留下美句和你的博客地址哦~ 戳这里看谁投食了 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |