对netlink无法检测到dellink事件和探测网卡是否插网线方法的简单分析
2011-10-26 22:56
519 查看
看了一下相关的实现,总结一下,有不对的请各位指正。
我看的source是kernel 2.6.27,ifplugd-0.28。
问题1:为啥用netlink检测网线插拔只能得到RTM_NEWLINK?
问题2:如何能检测到网卡插拔的信息。
netlink实现主要是在在net/netlink/af_netlink.c下面,但是rtnetlink.c在net/core下面。还有一个netlink.h在include/net下面。
用multicast作为关键字查一下,可以发现一个函数nlmsg_multicast,他的作用是multicast a netlink message。OK,顺着这个往上找。
函数调用关系:
nlmsg_multicast
^
|
nlmsg_notify
^
|
rtnl_notify
^
|
rtmsg_ifinfo
^
|
rtnetlink_event
^
|
rtnetlink_dev_notifier
到这里再往上就是register_netdevice_notifier(&rtnetlink_dev_notifier),到了通知链注册,不用往上看了,再往上就是初始化了。
在rtnetlink_event函数中有如下处理:
switch (event) {
case NETDEV_UNREGISTER:
rtmsg_ifinfo(RTM_DELLINK, dev, ~0U);
break;
case NETDEV_REGISTER:
rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
break;
case NETDEV_UP:
case NETDEV_DOWN:
rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING);
break;
case NETDEV_CHANGE:
case NETDEV_GOING_DOWN:
break;
default:
rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
break;
可以发现NETDEV_UP和NETDEV_DOWN都是同一处理,向rtmsg_ifinfo传的都是
RTM_NEWLINK,只有NETDEV_UNREGISTER传的是RTM_DELLINK。目前实验只有rmmod网卡驱动的时候会触发NETDEV_UNREGISTER。
问题1应该到此结束。
顺便说一下,调rtmsg_ifinfo的还有dev_change_flags函数,在处理ioctl的SIOCSIFFLAGS的时候使用这个函数。
问题2主要是ifplugd的四种方法(没考虑无线),在ifplugd.c中的work函数:
switch (api_mode) {
case API_ETHTOOL: detect_beat_func = interface_detect_beat_ethtool;
break;
case API_MII: detect_beat_func = interface_detect_beat_mii; break;
case API_PRIVATE: detect_beat_func = interface_detect_beat_priv;
break;
case API_WLAN: detect_beat_func = interface_detect_beat_wlan; break;
case API_IFF: detect_beat_func = interface_detect_beat_iff; break;
default:
detect_beat_func = detect_beat_auto;
Ifplugd在缺省配置时候用的是detect_beat_auto,这个函数其实就是把上面几个函数挨个试了一遍,先interface_detect_beat_ethtool再interface_detect_beat_mii。这四个方法都可以得到网卡的状态,但是实现有不同。
下面简单说说他们的实现:
方法1:interface_detect_beat_ethtool
调
ioctl的SIOCETHTOOL实现,net/core/dev.c里面的dev_ethtool函数处理。kernel中的ethtool机制,最
早是98年由David S.
Miller先搞出来的,这可是个牛人啊。主要实现在net/core/ethtool.c里面。Ifplugd使用ETHTOOL_GLINK命令来获
取网卡的状态,对这个命令的处理是ethtool_get_value函数:
case ETHTOOL_GLINK:
rc = ethtool_get_value(dev, useraddr, ethcmd,dev->ethtool_ops->get_link);
ethtool_get_value
里面回调dev->ethtool_ops->get_link来取网卡状态,这个方法需要各个网卡驱动自己实现,以intel
e100为例,他的实现在drivers/net/e100.c中的e100_get_link函数。e100_get_link调
mii_link_ok(deivers/net/mii.c),mii_link_ok的处理:
mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
return 1;
return 0;
mdio_read是mii需要各个驱动实现的一个callback,具体实现又回到了具体的网卡驱动,e100的在e100.c中的mdio_read函数。里面就是调体系结构相关的ioread32函数读网卡寄存器了。
方法2:interface_detect_beat_mii
调ioctl的SIOCGMIIPHY和SIOCGMIIREG实现,dev_ifsioc函数(net/core/dev.c)中处理:
if (dev->do_ioctl) {
if (netif_device_present(dev))
err = dev->do_ioctl(dev, ifr,cmd);
else
err = -ENODEV;
}
dev->do_ioctl
是各个网卡驱动自己实现的,以intel
e100为例,他的实现在drivers/net/e100.c中的e100_do_ioctl函数,e100_do_ioctl里面又调
generic_mii_ioctl(deivers/net/mii.c)。处理如下:
switch(cmd) {
case SIOCGMIIPHY:
mii_data->phy_id = mii_if->phy_id;
/* fall through */
case SIOCGMIIREG:
mii_data->val_out =
mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
mii_data->reg_num);
break;
很多网卡驱动都是调generic_mii_ioctl来处理ioctl的。MII定义
http://www.edacn.net/html/67/10867-75422.html
。看来网卡驱动监视和控制PHY基本都是用这个。关于mdio_read看上面方法1里面写的东东。
方法3:interface_detect_beat_priv
基
本上和方法2一样,调ioctl的SIOCDEVPRIVATE,和SIOCDEVPRIVATE+1实现,dev_ifsioc函数(net/core
/dev.c)中处理同方法2,有一点不同就是SIOCDEVPRIVATE到SIOCDEVPRIVATE +
15是各个网卡自己定义的命令,有的网卡有实现,有的没有。例如e100就没有实现这个ioctl。而rtl8150的usb版本网卡驱动就有实现。
方法4:interface_detect_beat_iff
调ioctl的SIOCGIFFLAGS实现,dev_ifsioc_locked函数(net/core/dev.c)中处理:
case SIOCGIFFLAGS: /* Get interface flags */
ifr->ifr_flags = dev_get_flags(dev);
return 0;
而dev_get_flags函数的处理:
flags = (dev->flags & ~(IFF_PROMISC |
IFF_ALLMULTI |
IFF_RUNNING |
IFF_LOWER_UP |
IFF_DORMANT)) |
(dev->gflags & (IFF_PROMISC |
IFF_ALLMULTI));
if (netif_running(dev)) {
if (netif_oper_up(dev))
flags |= IFF_RUNNING;
if (netif_carrier_ok(dev))
flags |= IFF_LOWER_UP;
if (netif_dormant(dev))
flags |= IFF_DORMANT;
没有由网卡驱动去查硬件,而是查现有的状态标志位来实现。
总结一下,方法1和2应该是最保险的,直接去查硬件寄存器,法3可能会有驱动不支持,法4不是很可靠。
我看的source是kernel 2.6.27,ifplugd-0.28。
问题1:为啥用netlink检测网线插拔只能得到RTM_NEWLINK?
问题2:如何能检测到网卡插拔的信息。
netlink实现主要是在在net/netlink/af_netlink.c下面,但是rtnetlink.c在net/core下面。还有一个netlink.h在include/net下面。
用multicast作为关键字查一下,可以发现一个函数nlmsg_multicast,他的作用是multicast a netlink message。OK,顺着这个往上找。
函数调用关系:
nlmsg_multicast
^
|
nlmsg_notify
^
|
rtnl_notify
^
|
rtmsg_ifinfo
^
|
rtnetlink_event
^
|
rtnetlink_dev_notifier
到这里再往上就是register_netdevice_notifier(&rtnetlink_dev_notifier),到了通知链注册,不用往上看了,再往上就是初始化了。
在rtnetlink_event函数中有如下处理:
switch (event) {
case NETDEV_UNREGISTER:
rtmsg_ifinfo(RTM_DELLINK, dev, ~0U);
break;
case NETDEV_REGISTER:
rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
break;
case NETDEV_UP:
case NETDEV_DOWN:
rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING);
break;
case NETDEV_CHANGE:
case NETDEV_GOING_DOWN:
break;
default:
rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
break;
可以发现NETDEV_UP和NETDEV_DOWN都是同一处理,向rtmsg_ifinfo传的都是
RTM_NEWLINK,只有NETDEV_UNREGISTER传的是RTM_DELLINK。目前实验只有rmmod网卡驱动的时候会触发NETDEV_UNREGISTER。
问题1应该到此结束。
顺便说一下,调rtmsg_ifinfo的还有dev_change_flags函数,在处理ioctl的SIOCSIFFLAGS的时候使用这个函数。
问题2主要是ifplugd的四种方法(没考虑无线),在ifplugd.c中的work函数:
switch (api_mode) {
case API_ETHTOOL: detect_beat_func = interface_detect_beat_ethtool;
break;
case API_MII: detect_beat_func = interface_detect_beat_mii; break;
case API_PRIVATE: detect_beat_func = interface_detect_beat_priv;
break;
case API_WLAN: detect_beat_func = interface_detect_beat_wlan; break;
case API_IFF: detect_beat_func = interface_detect_beat_iff; break;
default:
detect_beat_func = detect_beat_auto;
Ifplugd在缺省配置时候用的是detect_beat_auto,这个函数其实就是把上面几个函数挨个试了一遍,先interface_detect_beat_ethtool再interface_detect_beat_mii。这四个方法都可以得到网卡的状态,但是实现有不同。
下面简单说说他们的实现:
方法1:interface_detect_beat_ethtool
调
ioctl的SIOCETHTOOL实现,net/core/dev.c里面的dev_ethtool函数处理。kernel中的ethtool机制,最
早是98年由David S.
Miller先搞出来的,这可是个牛人啊。主要实现在net/core/ethtool.c里面。Ifplugd使用ETHTOOL_GLINK命令来获
取网卡的状态,对这个命令的处理是ethtool_get_value函数:
case ETHTOOL_GLINK:
rc = ethtool_get_value(dev, useraddr, ethcmd,dev->ethtool_ops->get_link);
ethtool_get_value
里面回调dev->ethtool_ops->get_link来取网卡状态,这个方法需要各个网卡驱动自己实现,以intel
e100为例,他的实现在drivers/net/e100.c中的e100_get_link函数。e100_get_link调
mii_link_ok(deivers/net/mii.c),mii_link_ok的处理:
mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
return 1;
return 0;
mdio_read是mii需要各个驱动实现的一个callback,具体实现又回到了具体的网卡驱动,e100的在e100.c中的mdio_read函数。里面就是调体系结构相关的ioread32函数读网卡寄存器了。
方法2:interface_detect_beat_mii
调ioctl的SIOCGMIIPHY和SIOCGMIIREG实现,dev_ifsioc函数(net/core/dev.c)中处理:
if (dev->do_ioctl) {
if (netif_device_present(dev))
err = dev->do_ioctl(dev, ifr,cmd);
else
err = -ENODEV;
}
dev->do_ioctl
是各个网卡驱动自己实现的,以intel
e100为例,他的实现在drivers/net/e100.c中的e100_do_ioctl函数,e100_do_ioctl里面又调
generic_mii_ioctl(deivers/net/mii.c)。处理如下:
switch(cmd) {
case SIOCGMIIPHY:
mii_data->phy_id = mii_if->phy_id;
/* fall through */
case SIOCGMIIREG:
mii_data->val_out =
mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
mii_data->reg_num);
break;
很多网卡驱动都是调generic_mii_ioctl来处理ioctl的。MII定义
http://www.edacn.net/html/67/10867-75422.html
。看来网卡驱动监视和控制PHY基本都是用这个。关于mdio_read看上面方法1里面写的东东。
方法3:interface_detect_beat_priv
基
本上和方法2一样,调ioctl的SIOCDEVPRIVATE,和SIOCDEVPRIVATE+1实现,dev_ifsioc函数(net/core
/dev.c)中处理同方法2,有一点不同就是SIOCDEVPRIVATE到SIOCDEVPRIVATE +
15是各个网卡自己定义的命令,有的网卡有实现,有的没有。例如e100就没有实现这个ioctl。而rtl8150的usb版本网卡驱动就有实现。
方法4:interface_detect_beat_iff
调ioctl的SIOCGIFFLAGS实现,dev_ifsioc_locked函数(net/core/dev.c)中处理:
case SIOCGIFFLAGS: /* Get interface flags */
ifr->ifr_flags = dev_get_flags(dev);
return 0;
而dev_get_flags函数的处理:
flags = (dev->flags & ~(IFF_PROMISC |
IFF_ALLMULTI |
IFF_RUNNING |
IFF_LOWER_UP |
IFF_DORMANT)) |
(dev->gflags & (IFF_PROMISC |
IFF_ALLMULTI));
if (netif_running(dev)) {
if (netif_oper_up(dev))
flags |= IFF_RUNNING;
if (netif_carrier_ok(dev))
flags |= IFF_LOWER_UP;
if (netif_dormant(dev))
flags |= IFF_DORMANT;
没有由网卡驱动去查硬件,而是查现有的状态标志位来实现。
总结一下,方法1和2应该是最保险的,直接去查硬件寄存器,法3可能会有驱动不支持,法4不是很可靠。
相关文章推荐
- C#中检测某个类(方法、程序集等各种部分)是否应用了指定的特性以及对特性的一些简单操作
- 简单方法检测电脑是否中病毒
- PHP简单检测网址是否能够正常打开的方法
- PHP简单检测网址是否能够正常打开的方法
- 如何用最简单最快的方法判断一个BMP图片是否为黑白图片?我现在是全图片scanline 然后再加以分析,太慢了,有没有直接的函数可以做到呢?
- jQuery动态添加元素无法触发绑定事件的解决方法分析
- 【Android】检测app是否安装、安装并打开的方法
- 如何检查mysql中建立的索引是否生效的检测方法及相关参数说明
- JQuery1.8 判断元素是否绑定事件的方法
- Vmware中克隆虚拟机后的克隆机网卡无法启动的解决方法
- 回发或回调参数无效。在配置中使用 或在页面中使用 启用了事件验证。出于安全目的,此功能验证回发或回调事件的参数是否来源于最初呈现这些事件的服务器控件。如果数据有效并且是预期的,则使用 ClientScriptManager.RegisterForEventValidation 方法来注册回发或回调数据以进行验证。
- jQuery检查事件是否触发的方法
- Android内存泄漏的简单检查与分析方法
- jquery事件委托的回调函数中调用全局变量的简单方法
- 【Visual Studio】简单内存泄漏检测方法 解决 Detected memory leaks! 问题(转)
- 判断一个字符串中是否含有中文的简单方法
- 解决“google快照无法打开”的简单而有效的方法~
- 简单的几种给定一个数判断是否是质数的方法
- PHP+Ajax实时自动检测是否联网的方法
- shell脚本检测mysql是否启动的方法