您的位置:首页 > 其它

对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不是很可靠。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐