• 网络设备之开启


    __dev_open函数,完成对设备的启用操作,并进行一些必要初始化和通知,调用关系如下,本文主要对这几个函数进行分析;

     1 /** 
     2  * _dev_open函数的调用关系
     3  * dev_change_flags-->_dev_change_flags-->__dev_open
     4  *
     5  * dev_open-->__dev_open
     6  *
     7  * __dev_open
     8  *       |-->dev_set_rx_mode
     9  *                |-->__dev_set_promiscuity
    10  */

    在标志改变时,__dev_change_flags会对新旧标志进行检查处理,若果发现其IFF_UP标识位有所变化,则根据其原来是否处理开启状态做对应处理,若原来处理启用状态,则关闭之,若原来处理关闭状态,则开启之;

     1 int __dev_change_flags(struct net_device *dev, unsigned int flags)
     2 {
     3     unsigned int old_flags = dev->flags;
     4     int ret;
     5 
     6     /* 这里省去一些无关的代码细节 */
     7 
     8     /*
     9      *    Have we downed the interface. We handle IFF_UP ourselves
    10      *    according to user attempts to set it, rather than blindly
    11      *    setting it.
    12      */
    13 
    14     ret = 0;
    15     /* 两个标识有一个是IFF_UP */
    16     if ((old_flags ^ flags) & IFF_UP)
    17         /* 源标识有IFF_UP则调用关闭,否则调用开启 */
    18         ret = ((old_flags & IFF_UP) ? __dev_close : __dev_open)(dev);
    19 
    20    /* 这里省去一些无关的代码细节 */
    21 
    22     return ret;
    23 }

    dev_open完成设备的启用,其首先会进行当前状态的判断,若处于关闭状态,则执行启用设备操作,并在启用后发送通知消息;

     1 /**
     2  *    dev_open    - prepare an interface for use.
     3  *    @dev:    device to open
     4  *
     5  *    Takes a device from down to up state. The device's private open
     6  *    function is invoked and then the multicast lists are loaded. Finally
     7  *    the device is moved into the up state and a %NETDEV_UP message is
     8  *    sent to the netdev notifier chain.
     9  *
    10  *    Calling this function on an active interface is a nop. On a failure
    11  *    a negative errno code is returned.
    12  */
    13 int dev_open(struct net_device *dev)
    14 {
    15     int ret;
    16 
    17     /* 如果已经打开返回 */
    18     if (dev->flags & IFF_UP)
    19         return 0;
    20 
    21     /* 打开设备 */
    22     ret = __dev_open(dev);
    23     if (ret < 0)
    24         return ret;
    25 
    26     /* 通知设备打开 */
    27     rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING, GFP_KERNEL);
    28     call_netdevice_notifiers(NETDEV_UP, dev);
    29 
    30     return ret;
    31 }

    __dev_open为设备启用核心函数,该函数执行设备启用,设置启用标记,并且设置接收模式,排队规则等;

     1 static int __dev_open(struct net_device *dev)
     2 {
     3     const struct net_device_ops *ops = dev->netdev_ops;
     4     int ret;
     5 
     6     ASSERT_RTNL();
     7 
     8     /* 设备不可用 */
     9     if (!netif_device_present(dev))
    10         return -ENODEV;
    11 
    12     /* Block netpoll from trying to do any rx path servicing.
    13      * If we don't do this there is a chance ndo_poll_controller
    14      * or ndo_poll may be running while we open the device
    15      */
    16     /* 禁用netpoll */
    17     netpoll_poll_disable(dev);
    18 
    19     /* 设备打开前通知 */
    20     ret = call_netdevice_notifiers(NETDEV_PRE_UP, dev);
    21     ret = notifier_to_errno(ret);
    22     if (ret)
    23         return ret;
    24 
    25     /* 设置设备打开标记 */
    26     set_bit(__LINK_STATE_START, &dev->state);
    27 
    28     /* 校验地址 */
    29     if (ops->ndo_validate_addr)
    30         ret = ops->ndo_validate_addr(dev);
    31 
    32     /* 执行打开 */
    33     if (!ret && ops->ndo_open)
    34         ret = ops->ndo_open(dev);
    35 
    36     /* 启用netpoll */
    37     netpoll_poll_enable(dev);
    38 
    39     /* 失败,清除打开标记 */
    40     if (ret)
    41         clear_bit(__LINK_STATE_START, &dev->state);
    42 
    43     /* 设备打开操作 */
    44     else {
    45         /* 设置打开标记 */
    46         dev->flags |= IFF_UP;
    47 
    48         /* 设置接收模式 */
    49         dev_set_rx_mode(dev);
    50         /* 初始化排队规则 */
    51         dev_activate(dev);
    52         /* 加入设备数据到熵池 */
    53         add_device_randomness(dev->dev_addr, dev->addr_len);
    54     }
    55 
    56     return ret;
    57 }

    设备启用过程中,会设置接收模式,若设备实现了ndo_set_rx_mode则调用设备的该函数进行模式设置,如果设备没有实现该函数,那么将会根据单播和混杂标志进行设置;

    关于该问题的详细介绍,请参考本博客的另外一篇文章<网络设备之uc_promisc>

     1 /*
     2  *    Upload unicast and multicast address lists to device and
     3  *    configure RX filtering. When the device doesn't support unicast
     4  *    filtering it is put in promiscuous mode while unicast addresses
     5  *    are present.
     6  */
     7 void __dev_set_rx_mode(struct net_device *dev)
     8 {
     9     const struct net_device_ops *ops = dev->netdev_ops;
    10 
    11     /* dev_open will call this function so the list will stay sane. */
    12     /* 设备未启动 */
    13     if (!(dev->flags&IFF_UP))
    14         return;
    15 
    16     /* 设备不存在 */
    17     if (!netif_device_present(dev))
    18         return;
    19 
    20     /* 不支持单播过滤 */
    21     /* 未实现ndo_set_rx_mode */
    22     if (!(dev->priv_flags & IFF_UNICAST_FLT)) {
    23         /* Unicast addresses changes may only happen under the rtnl,
    24          * therefore calling __dev_set_promiscuity here is safe.
    25          */
    26         /* 单播硬件地址存在&& 单播混杂模式未开启 */
    27         if (!netdev_uc_empty(dev) && !dev->uc_promisc) {
    28             /* 开启混杂模式 */
    29             __dev_set_promiscuity(dev, 1, false);
    30             dev->uc_promisc = true;
    31         }
    32         /* 单播硬件地址不存在&& 设备开启混杂模式  */
    33         else if (netdev_uc_empty(dev) && dev->uc_promisc) {
    34             /*  */
    35             __dev_set_promiscuity(dev, -1, false);
    36             dev->uc_promisc = false;
    37         }
    38     }
    39 
    40     /* 调用设备设置接收模式函数 */
    41     if (ops->ndo_set_rx_mode)
    42         ops->ndo_set_rx_mode(dev);
    43 }

    上面函数分支会调用__dev_set_promiscuity函数设置混杂模式,该函数根据混杂模式计数开启或者关闭设备的混杂模式;

     1 /* 设置混杂模式 */
     2 static int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify)
     3 {
     4     unsigned int old_flags = dev->flags;
     5     kuid_t uid;
     6     kgid_t gid;
     7 
     8     ASSERT_RTNL();
     9 
    10     /* 打混杂标记 */
    11     dev->flags |= IFF_PROMISC;
    12 
    13     /* 改变混杂计数,inc可能为负 */
    14     dev->promiscuity += inc;
    15 
    16     /* 所有混杂均释放 */
    17     if (dev->promiscuity == 0) {
    18         /*
    19          * Avoid overflow.
    20          * If inc causes overflow, untouch promisc and return error.
    21          */
    22         /* 当前是释放混杂计数操作,则关闭混杂模式 */
    23         if (inc < 0)
    24             dev->flags &= ~IFF_PROMISC;
    25         /* 否则出错 */
    26         else {
    27             dev->promiscuity -= inc;
    28             pr_warn("%s: promiscuity touches roof, set promiscuity failed. promiscuity feature of device might be broken.
    ",
    29                 dev->name);
    30             return -EOVERFLOW;
    31         }
    32     }
    33 
    34     /* 新旧标识不相同 */
    35     if (dev->flags != old_flags) {
    36         pr_info("device %s %s promiscuous mode
    ",
    37             dev->name,
    38             dev->flags & IFF_PROMISC ? "entered" : "left");
    39         if (audit_enabled) {
    40             current_uid_gid(&uid, &gid);
    41             audit_log(current->audit_context, GFP_ATOMIC,
    42                 AUDIT_ANOM_PROMISCUOUS,
    43                 "dev=%s prom=%d old_prom=%d auid=%u uid=%u gid=%u ses=%u",
    44                 dev->name, (dev->flags & IFF_PROMISC),
    45                 (old_flags & IFF_PROMISC),
    46                 from_kuid(&init_user_ns, audit_get_loginuid(current)),
    47                 from_kuid(&init_user_ns, uid),
    48                 from_kgid(&init_user_ns, gid),
    49                 audit_get_sessionid(current));
    50         }
    51 
    52         /* 调用改变rx标识操作 */
    53         dev_change_rx_flags(dev, IFF_PROMISC);
    54     }
    55 
    56     /* 混杂模式通知 */
    57     if (notify)
    58         __dev_notify_flags(dev, old_flags, IFF_PROMISC);
    59     return 0;
  • 相关阅读:
    模拟队列
    代理模式及java简易实现
    归并排序模板(Java)
    快排Java模板
    durid配置jdbc报错 com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server
    File类
    数据库范式、多表查询,事务
    valuestack(值栈) 和 actioncontext(上下文)
    Action
    Cookie 和Session
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/7526398.html
Copyright © 2020-2023  润新知