VLAN数据包接收流程

先来看一下vlan数据包的帧格式,整个vlan信息大小为4个字节,分别为2个字节的标签协议标识(Tag Protocol Identifier),和2个字节标签控制信息(Tag Control Infomation)。

其中后者TCI又有三个子字段组成:3个bit的优先级(PRI)、一个bit的标准格式指示器(Canonical Format Indicator)和12个bit的vlan id:


以下代码是位于net/core/dev.c文件中vlan数据包的主要接收处理函数。驱动层接收上来的数据包已经设置好了skb的protocol字段(为8021Q或者8021AD)。依次调用skb_vlan_untag与vlan_do_receive函数进行处理。


static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
{
another_round:

    if (skb->protocol == cpu_to_be16(ETH_P_8021Q) ||
        skb->protocol == cpu_to_be16(ETH_P_8021AD)) {
        skb = skb_vlan_untag(skb);
    }
    if (skb_vlan_tag_present(skb)) {
        if (vlan_do_receive(&skb))
            goto another_round;
    }
}


剥离VLAN标签


对于函数skb_vlan_untag来说,主要功能是从数据包中提取vlan信息,保存到skb结构中,然后从数据包中取出vlan相关字段,另外,获取数据包的真实协议类型更新到skb的protocol字段(三层协议类型),替换了之前的ETH_P_8021Q或者ETH_P_8021AD。

skb_reorder_vlan_header函数负责将vlan信息从数据包中剥离出来。


static inline void vlan_set_encap_proto(struct sk_buff *skb, struct vlan_hdr *vhdr)
{
    proto = vhdr->h_vlan_encapsulated_proto;
    if (ntohs(proto) >= ETH_P_802_3_MIN)
        skb->protocol = proto;
}
static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb)
{
    memmove(skb->data - ETH_HLEN, skb->data - skb->mac_len - VLAN_HLEN, 2 * ETH_ALEN);
}
struct sk_buff *skb_vlan_untag(struct sk_buff *skb)
{
    vhdr = (struct vlan_hdr *)skb->data;
    vlan_tci = ntohs(vhdr->h_vlan_TCI);
    __vlan_hwaccel_put_tag(skb, skb->protocol, vlan_tci);

    vlan_set_encap_proto(skb, vhdr);
	
	skb = skb_reorder_vlan_header(skb);
}


VLAN设备接收


系统中必须要有与数据包中vlan id相同的vlan设备,否则结束处理。找到此vlan设备后将其赋值给skb的dev,此时就完成了接收设备从物理网卡到vlan设备的转换,skb中的vlan_tci也就没有用处了可以清理。VLAN设备可能具有与其所在物理设备不同的MAC地址,在此情况下物理设备驱动程序会赋值PACKET_OTHERHOST到skb的pkt_type,需要进一步判断数据包目的MAC是否为vlan的MAC地址,如果是,修改pkt_type为PACKET_HOST,表示为发往本机的数据包。

如果关闭VLAN_FLAG_REORDER_HDR选项,vlan_do_receive函数会重新把vlan信息插入到skb的payload中。

bool vlan_do_receive(struct sk_buff **skbp)
{
    vlan_dev = vlan_find_dev(skb->dev, vlan_proto, vlan_id);
    if (!vlan_dev) return false;

    skb->dev = vlan_dev;
    if (skb->pkt_type == PACKET_OTHERHOST) {
        if (ether_addr_equal(eth_hdr(skb)->h_dest, vlan_dev->dev_addr))
            skb->pkt_type = PACKET_HOST;
    }
    skb->vlan_tci = 0;
}


对于IP数据包,skb的protocol换成了0x0806,ip_rcv函数就可以正常接收了。


内核版本

Linux-4.15


  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
很抱歉,我作为一个语言模型无法直接截屏,但我可以提供如下步骤来实现 VLAN 间的路由,并观察数据包的转发过程: 1. 配置 VLAN 首先,在交换机上创建两个 VLAN,命名为 VLAN10 和 VLAN20,并将 PC1 和 PC3 分别加入 VLAN10,将 PCO 和 PC2 分别加入 VLAN20。具体的 VLAN 配置方法请参考前面提到的配置 VLAN 的步骤。 2. 配置路由器 接下来,需要配置路由器来实现 VLAN 间的路由。路由器可以采用双接口模式或单接口模式。 双接口模式下,路由器需要至少两个网卡,每个网卡连接一个交换机的 VLAN。例如,将路由器的 eth0 连接到 VLAN10,将路由器的 eth1 连接到 VLAN20。然后,需要在路由器上配置 IP 地址,并启用 IP 路由功能。具体的配置方法如下: ``` interface eth0 ip address 192.168.1.1 255.255.255.0 no shutdown interface eth1 ip address 192.168.2.1 255.255.255.0 no shutdown ip routing ``` 单接口模式下,路由器只需要一个网卡,连接到交换机的一个端口上。需要在路由器上创建子接口,并为每个子接口分配一个 VLAN 号。具体的配置方法如下: ``` interface eth0.10 encapsulation dot1q 10 ip address 192.168.1.1 255.255.255.0 no shutdown interface eth0.20 encapsulation dot1q 20 ip address 192.168.2.1 255.255.255.0 no shutdown ip routing ``` 3. 测试 完成 VLAN 和路由器的配置后,可以通过 ping 命令来测试 VLAN 间的连通性。具体的测试步骤如下: 1. 在 PC1 上执行 `ping 192.168.2.2`,这将向 PC3 发送 ICMP 数据包。如果 VLAN 和路由器的配置正确,那么 PC1 应该能够收到回复,ICMP 数据包经过路由器转发到 PC3。 2. 在 PCO 上执行 `ping 192.168.1.2`,这将向 PC2 发送 ICMP 数据包。如果 VLAN 和路由器的配置正确,那么 PCO 应该能够收到回复,ICMP 数据包经过路由器转发到 PC2。 3. 在 PC1 上执行 `ping 192.168.1.2`,这将向 VLAN20 中的 IP 地址发送 ICMP 数据包。如果 VLAN 和路由器的配置正确,那么 PC1 应该无法收到回复,ICMP 数据包被路由器丢弃。 4. 在 PCO 上执行 `ping 192.168.2.2`,这将向 VLAN10 中的 IP 地址发送 ICMP 数据包。如果 VLAN 和路由器的配置正确,那么 PCO 应该无法收到回复,ICMP 数据包被路由器丢弃。 通过以上步骤,可以观察同一个 VLAN 和不同 VLAN数据包转发过程,并测试 VLAN 间的连通性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值