基于DPDK实现二层交换

上次分析了DPDK的l2fwd的源码,l2fwd实现了相邻两个端口之间的二层交换,入端口和出端口的关系是通过一个数组来实现的,从某一个端口进只能从一个固定的端口出。于是想到对其转发的规则做出修改就能实现一个二层交换机,也就是说在l2fwd中创建一个mac表,这个表中存储了每一个端口上的主机的mac地址,然后当l2fwd收到一个数据包的时候就能够根据mac表将数据包从对应的端口转发出去从而实现了二层交换的功能,当然这个mac表的内容是需要l2fwd根据实际网络来自学习的,结合上次总结的两个主机在二层交换情况下的通信过程可以知道,交换机是根据主机的ARP数据包的源mac地址来学习主机的位置的,所以只要按照这样的逻辑对l2fwd改造就能实现一个二层交换机。

实现步骤

1. 创建mac表

mac表主要记录了每一个端口上的主机的mac地址,因此可以创建如下的结构体来实现

// 添加结构体
#define MAX_HOST_PER_PORT 100
struct host_in_this_port{
	unsigned nb_host; // host的数量
	struct ether_addr host_list[MAX_HOST_PER_PORT]; // mac地址的数组
};
struct host_in_this_port all_port_host[RTE_MAX_ETHPORTS];// 创建一个host_in_this_port结构体数组 存放这个端口的host列表

并且要初始化这个结构体的一个全局的数组来存放每一个端口上的主机的mac地址

2. 交换机自学习过程

自学习过程也就是填充刚刚创建的all_port_host数组的过程,根据二层交换的过程,对主机的位置的学习主要是根据ARP包的源地址来实现的,也就是说在哪个端口上收到了ARP包,那么这个ARP包的源MAC所对应的主机的位置就在这个端口上,代码如下

    struct ether_addr eth_src = eth->s_addr;
    struct ether_addr eth_dst = eth->d_addr;
    uint16_t type = eth->ether_type;    

    struct host_in_this_port *this_port = NULL;
    if (type = 0x0806)
    {

        // 根据数据链路层的源mac学习每一个主机的位置
        // 将这个源mac添加到portid的host_in_this_port的结构体中
        this_port = &all_port_host[portid];
        int host_nb = this_port->nb_host;
        int count = 0;
        int exist = 0;
        for (count = 0; count < host_nb; count++)
        {
            if (isMacAddrEqual(this_port -> host_list[count], eth_src))
            {
                exist = 1;
                break;
            }
        }
        if (exist == 0)
        {
            this_port->host_list[this_port->nb_host] = eth_src;
            this_port->nb_host++;
        }
        else
        {
            exist = 0;
        }
    }

首先从数据链路层的头部获取ethernet的类型,如果是ARP包就执行这个学习的过程,首先取出这个数据包的源MAC地址,然后从对应的端口的host_in_this_port结构体中去寻找是否之前已经学习到了这个mac地址,如果没有的话就将这个mac地址添加到这个结构体中

3. 转发过程

转发主要是根据收到的数据包的目的地址来转发的,根据数据包的目的MAC地址,如果是广播地址的话就将这个数据包从除了收到的这个数据包的端口以外的所有端口发送除去,如果不是广播地址的话就查询all_port_host结构体数组,如果在某一个端口的主机mac地址列表中查询到了这个目的mac地址,就证明这个主机在这个端口上,那么就把数据包从这个端口发送出去,如果没有查询到那么就将数据包广播出去

    uint16_t port_num = rte_eth_dev_count();
    if (isMacAddrBroadcast(eth_dst))
    {
        flood(portid, port_num, m);
    }
    else
    {
        int flag = 0;
        // 找到一个port 这个port上有目的mac的主机
        int id = 0;
        for (id = 0; id < port_num; id++) // 遍历每一个端口
        {
            if ((l2fwd_enabled_port_mask & (1 << id)) == 0)
            {
                continue;
            }
            // 遍历连接这个端口的所有的主机
            int mac_id = 0;
            for (mac_id = 0; mac_id < all_port_host[id].nb_host; mac_id++)
            {
                if (isMacAddrEqual(all_port_host[id].host_list[mac_id], eth_dst))
                {
                    flag = 1;
                    buffer = tx_buffer[id];
                    sent = rte_eth_tx_buffer(id, 0, buffer, m); // 放入到buffer中
                    if (sent)
                    {
                        port_statistics[id].tx += sent;
                    }
                    break;
                }
            }
            if (flag)
            {
                break;
            }
        }
        // 没有找到对应的主机就洪范
        if (flag == 0)
        {
            flood(portid, port_num, m);
        }
        else
        {
            flag = 0;
        }
    }

将学习过程和转发过程添加到l2fwd_simple_forward函数当中,就可以实现二层交换的功能了,同时需要注意把l2fwd_main_loop函数中调用rte_eth_tx_buffer_flush函数的地方的循环修改如下

            for (i = 0; i < qconf->n_rx_port; i++)
            {
                buffer = tx_buffer[i];
                sent = rte_eth_tx_buffer_flush(i, 0, buffer);
                if (sent)
                    port_statistics[i].tx += sent;
            }

原来的是根据当前端口根据数组确定的目的端口数据发送出去,由于现在的交换功能每一个端口上的发送队列上都可能缓存有数据,因此需要遍历每一个端口,并将数据发送出去,这样就实现了一个二层交换的功能。

试验测试

在gns3下进行测试,测试拓扑如下

将l2fwd放到虚拟机上并加载到拓扑当中,开启4个端口,打开虚拟机并对dpdk进行配置,设置大页,加载uio驱动等,并将4个网卡交给dpdk管理

然后开启l2fwd,命令为./l2fwd -- -q 4 –p 0xf,虚拟机启动了两个逻辑核,一个是master核,另一个是slave,slave是用来处理业务的,虚拟机上有4个端口,因此需要一个逻辑核绑定4个端口,并且这4个端口都是启用的所以是-p 0xf,启动后,为各个VPCS设置IP地址,设置IP地址的时候会发送ARP的广播,此时我们可以看到,l2fwd已经学习到了相关主机的mac地址(在print_stats里添加了打印mac表的代码)

测试一下各个主机之间都是可以ping通的

抓包结果也是正常的,目的mac地址是广播地址的数据包会广播到各个链路上,两主机之间的ping产生的数据包也只会在对应的链路上出现,而不会发送到其他的链路上。例如在pc1的链路上进行抓包,让pc2 ping pc4的时候pc1的链路上只会收到一个ARP的广播包,让pc2 ping pc1的时候则会抓到对应的ICMP包

多个交换机连接在一起也是可以跑通的,构建下述拓扑图

配置好ip后查看两个交换机的mac表,可以看到两个交换机的3号端口上有多个主机的mac地址,说明交换机也可以学习到对侧的主机的mac地址

 

主机之间跨交换机也是可以通信的

当然这种简单的实现是存在问题的,让两个交换机连成环路是会出现广播风暴的。当一个主机发出目的mac地址是广播地址的时候,如果这个数据包发给构成环路的交换机,交换机又会对这个数据包进行广播,同时环路中的交换机收到数据包以后也会对其广播,最终造成广播风暴,构建如下拓扑图,两个交换机之间互相连接构成环路(实际可能是3个以上交换机构成环路,电脑性能有限,就这样模拟一下)

这个时候如果PC-1发出ARP广播包后在环路上进行抓包,可以看到大量的ARP广播包形成广播风暴(当时电脑风扇转的飞起,感觉快炸了,赶紧关掉了)

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值