0
点赞
收藏
分享

微信扫一扫

Linux内核中VLAN的实现过程(6)-设备打开、关闭和数据发送

Linux内核中VLAN的实现过程(6)

本节主要关注和解析vlan设备的打开、关闭以及数据发送实现,代码位于net/8021q/vlan_dev.c文件中。

打开设备

static int vlan_dev_open(struct net_device *dev)
{
    // 获取vlan设备的私有信息
    struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
    // 宿主设备
    struct net_device *real_dev = vlan->real_dev;
    int err;

    if (!(real_dev->flags & IFF_UP) &&
        !(vlan->flags & VLAN_FLAG_LOOSE_BINDING))
        return -ENETDOWN;

    // 检查设备mac地址:vlan设备mac地址等于宿主设备mac地址
    if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr) &&
        !vlan_dev_inherit_address(dev, real_dev)) {
        // 为vlan设备添加第二个单播mac地址(Add a secondary unicast address to the device or increase the reference count if it already exists.)
        err = dev_uc_add(real_dev, dev->dev_addr);
        if (err < 0)
            goto out;
    }

    // allmulti如promiscuity一样,是一个计数器而不是简单的布尔值。当此变量由0变为非0时,就会调用dev_set_allmulti函数,以指示该端口监听所有的多播地址;当allmulti变为0时则反之。
    if (dev->flags & IFF_ALLMULTI) {
        // 设备监听所有多播地址
        err = dev_set_allmulti(real_dev, 1);
        if (err < 0)
            goto del_unicast;
    }
    if (dev->flags & IFF_PROMISC) {
        // 开启混杂模式
        err = dev_set_promiscuity(real_dev, 1);
        if (err < 0)
            goto clear_allmulti;
    }

    // 宿主设备的mac保存到vlan设备私有信息中
    ether_addr_copy(vlan->real_dev_addr, real_dev->dev_addr);

    // 加入GARP VLAN注册协议
    if (vlan->flags & VLAN_FLAG_GVRP)
        vlan_gvrp_request_join(dev);

    // 加入Multiple VLAN注册协议
    if (vlan->flags & VLAN_FLAG_MVRP)
        vlan_mvrp_request_join(dev);

    /*
    网卡在物理上具有载波侦听的功能,当网络连接完整或者网络链接断开时,网卡芯片硬件会自动设置寄存器标志位来标识。
    如网线链接断开的时候,会将LinkSts清位;重新链接网线,则硬件自动将此位置位。
    这样,在网卡驱动中读写该位信息就可一判断网络是否链接通路。
    网卡驱动程序通过netif_carrier_on/netif_carrier_off/netif_carrier_ok来和内核网络子系统传递信息。
    netif_carrier_on:告诉内核子系统网络链接完整。
    netif_carrier_off:告诉内核子系统网络断开。
    netif_carrier_ok:查询网络断开还是链接。
    */
    // 设置网络连接
    if (netif_carrier_ok(real_dev) &&
        !(vlan->flags & VLAN_FLAG_BRIDGE_BINDING))
        netif_carrier_on(dev);
    return 0;

clear_allmulti:
    if (dev->flags & IFF_ALLMULTI)
        dev_set_allmulti(real_dev, -1);
del_unicast:
    if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr))
        dev_uc_del(real_dev, dev->dev_addr);
out:
    // 设置网络断开 
    netif_carrier_off(dev);
    return err;
}

关闭设备

static int vlan_dev_stop(struct net_device *dev)
{
    struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
    struct net_device *real_dev = vlan->real_dev;

    // 删除由 dev_mc_sync() 添加到目标设备的所有地址
    dev_mc_unsync(real_dev, dev);
    // 删除由 dev_uc_sync() 添加到目标设备的所有地址
    dev_uc_unsync(real_dev, dev);
    // 关闭多播
    if (dev->flags & IFF_ALLMULTI)
        dev_set_allmulti(real_dev, -1);
    // 关闭混杂
    if (dev->flags & IFF_PROMISC)
        dev_set_promiscuity(real_dev, -1);

    // 移除单播mac地址
    if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr))
        dev_uc_del(real_dev, dev->dev_addr);

    // 设置网络断开
    if (!(vlan->flags & VLAN_FLAG_BRIDGE_BINDING))
        netif_carrier_off(dev);
    return 0;
}

发送数据

static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
                        struct net_device *dev)
{
    struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
    // 带vlan的以太网头
    struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data);
    unsigned int len;
    int ret;

    /* Handle non-VLAN frames if they are sent to us, for example by DHCP.
     *
     * NOTE: THIS ASSUMES DIX ETHERNET, SPECIFICALLY NOT SUPPORTING
     * OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs...
     */
    if (veth->h_vlan_proto != vlan->vlan_proto ||
        vlan->flags & VLAN_FLAG_REORDER_HDR) {
        // 无vlan tag,则添加vlan tag
        u16 vlan_tci;
        vlan_tci = vlan->vlan_id;
        // 获取出口qos优先级
        vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority);
        // 添加vlan tag
        __vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci);
    }

    // 修改数据包发送网卡为宿主设备
    skb->dev = vlan->real_dev;
    len = skb->len;
    // 如果ndo_start_xmit是由netpool调用,则返回非零值
    if (unlikely(netpoll_tx_running(dev)))
        // 使用netpool发送数据包
        return vlan_netpoll_send_skb(vlan, skb);

    // 将数据包加入发送队列
    ret = dev_queue_xmit(skb);

    // dev_queue_xmit返回成功或者拥塞
    if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
        struct vlan_pcpu_stats *stats;

        stats = this_cpu_ptr(vlan->vlan_pcpu_stats);
        // 序列计数器
        u64_stats_update_begin(&stats->syncp);
        // 更新设备统计信息:发包个数和发包字节数
        stats->tx_packets++;
        stats->tx_bytes += len;
        u64_stats_update_end(&stats->syncp);
    } else {
        // 丢弃,并更新设备统计信息:丢包个数
        this_cpu_inc(vlan->vlan_pcpu_stats->tx_dropped);
    }

    return ret;
}

static inline netdev_tx_t vlan_netpoll_send_skb(struct vlan_dev_priv *vlan, struct sk_buff *skb)
{
#ifdef CONFIG_NET_POLL_CONTROLLER
    // 使用netpool发送数据包
    return netpoll_send_skb(vlan->netpoll, skb);
#else
    BUG();
    return NETDEV_TX_OK;
#endif
}
举报

相关推荐

0 条评论