Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1835769
  • 博文数量: 241
  • 博客积分: 9862
  • 博客等级: 中将
  • 技术积分: 5206
  • 用 户 组: 普通用户
  • 注册时间: 2005-02-18 23:23
文章分类
文章存档

2011年(14)

2010年(61)

2009年(48)

2008年(118)

我的朋友

分类: LINUX

2009-10-29 11:43:27

看了一下相关的实现,总结一下,有不对的请各位指正。
我看的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定义。看来网卡驱动监视和控制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不是很可靠。
--
阅读(3618) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~