一、 Linux对VLAN数据的处理
(1)VLAN初始化
static struct packet_type vlan_packet_type __read_mostly = {
.type =cpu_to_be16(ETH_P_8021Q),
.func = vlan_skb_recv, /*VLAN receive method */
};
vlan_proto_init调用dev_add_pack(&vlan_packet_type)函数进行VLAN的注册,把ETH_P_8021Q 处理函数挂在ptype_base下,即数据接收的VLAN的入口函数为vlan_skb_recv
(2)VLAN数据处理
内核中的vlan模块就是工作在协议栈里,通过注册dev_add_pack注册了8021Q的协议类型,netif_receive_skb->__netif_receive_skb遍历所有的协议类型时发现了它,于是进入了vlan的接收函数vlan_skb_recv(vlan_dev.c),这个函数里剥离vlan层,重置以太网层的proto,重新调用netif_rx进入真正的协议栈处理流程。
对于Linux,无论什么数据包通过网卡驱动后都会进入__netif_receive_skb函数。该函数中对VLAN的处理如下:
static int__netif_receive_skb(struct sk_buff *skb)
{
… …
//遍历ptye_all链表
list_for_each_entry_rcu(ptype, &ptype_all,list) {
if (ptype->dev == null_or_orig || ptype->dev ==skb->dev ||
ptype->dev ==orig_dev) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
}
… …
// bridge逻辑
/* Handle special case of bridge or macvlan */
rx_handler = rcu_dereference(skb->dev->rx_handler);
if (rx_handler) {
if (pt_prev) {
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = NULL;
}
skb = rx_handler(skb);
if (!skb)
goto out;
}
… …
//遍历ptype_base
type = skb->protocol; //如果是VLAN,则这里type被置为VLAN协议,即0x8100(经过VLAN处理后,protocol会被重置)
list_for_each_entry_rcu(ptype,
&ptype_base[ntohs(type) & PTYPE_HASH_MASK],list) {
if (ptype->type == type && (ptype->dev ==null_or_orig ||
ptype->dev ==skb->dev || ptype->dev == orig_dev ||
ptype->dev ==orig_or_bond)) {
if (pt_prev){
// deliver_skb函数最终调用paket_type.func(),如果是VLAN,由于type为802.1Q的协议,所以会调用其对应的协议处理函数:vlan_skb_recv
ret = deliver_skb(skb, pt_prev, orig_dev);
}
pt_prev = ptype;
}
}
… …
}
二、 vlan_skb_recv函数解析
定义几个vlan接口和vlan ID相关的宏:
#define MTC_STP_PACKET_VLAN_ID 4094
#define MTC_WAN_VLAN_ID 2
#define MTC_LAN_VLAN_ID 1
#define MTC_WAN_IFNAME "eth2.2"
#define MTC_LAN_IFNAME "eth2.1"
int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype, struct net_device*orig_dev)
{
… …
// 从skb解析出对应的vlan_id
vhdr = (struct vlan_hdr*)skb->data;
vlan_tci =ntohs(vhdr->h_vlan_TCI);
vlan_id = vlan_tci &VLAN_VID_MASK;
… …
//这里dev指向的是真实的dev,通过__find_vlan_dev函数根据vlan_id找到对应接口的dev(比如现在用的系统真实dev接口名称为eth2; vlan_id==1的接口名称为eth2.1; vlan_id==2的接口名称为eth2.2)
vlan_dev =__find_vlan_dev(dev, vlan_id);
//以下处理是为了确保vlan_id为非1和非2的时候数据包不会被丢掉
#ifdef PRODUCT_MT7628STPAP_MTC
/*
There is not any device attachto the MTC_STP_PACKET_VLAN_ID(4094) vlan id,
In order to pass the normalprocessing of vlan_skb_recv function,
we set the vlan_dev toMTC_WAN_IFNAME(eth2.2).
by wwk,20170426
*/
if(MTC_STP_PACKET_VLAN_ID ==vlan_id_back)
{
vlan_dev = __dev_get_by_name(&init_net,MTC_LAN_IFNAME);
}
#endif
/* If the VLAN device isdefined, we use it.
* If not, and the VID is 0, it is a 802.1ppacket (not
* really a VLAN), so we will just netif_rx itlater to the
* original interface, but with the skb->protoset to the
* wrapped proto: we do nothing here.
*/
if (!vlan_dev) {
if (vlan_id) {
pr_debug("%s:ERROR: No net_device for VID: %u on dev: %sn",
__func__, vlan_id, dev->name);
//如果通过上面的函数__find_vlan_dev没找到vlan_dev,则把数据包扔掉,当前系统默认仅支持MTC_WAN_IFNAME和MTC_LAN_IFNAME,即VLAN ID为2和VLAN ID为1的VLAN
goto err_unlock;
}
else
{
… …
}
} else {
… …
}
//以下为对从WAN进来的包进行处理:从WAN进来的包copy一份同步发到LAN端,发送之前把把vlan_id设置为4096
#ifdef PRODUCT_MT7628STPAP_MTC
/*
if pkg from vlanMTC_WAN_VLAN_ID, then forward one copy to MTC_LAN_IFNAME(vlan_id isMTC_STP_PACKET_VLAN_ID)
<wan-->add 4096vlan_tag-->vlan 4096-->send pkg from MTC_LAN_IFNAME interface>
Must be called beforeskb_pull_rcsum() ,vlan_set_encap_proto() and vlan_check_reorder_header()
by wwk,20170426
*/
… … //实现细节
#endif
//使用以下三个函数用来剥离VLAN层和重置以太网层的proto: skb_pull_rcsum、vlan_set_encap_proto、vlan_check_reorder_header
skb_pull_rcsum(skb,VLAN_HLEN);
vlan_set_encap_proto(skb,vhdr);
if (vlan_dev) {
skb = vlan_check_reorder_header(skb);
if (!skb) {
rx_stats->rx_errors++;
goto err_unlock;
}
}
#ifdef PRODUCT_MT7628STPAP_MTC
if(MTC_LAN_VLAN_ID ==vlan_id_back)
{
// 从MTC_LAN_IFNAME进来的数据包,视情况对VLAN进行处理
… … //实现细节
}
//if pkg from vlanMTC_STP_PACKET_VLAN_ID, then forward to MTC_WAN_IFNAME
if(MTC_STP_PACKET_VLAN_ID == vlan_id_back)
{
… … //实现细节
/*
There is not any deviceattach to the MTC_STP_PACKET_VLAN_ID(4094) vlan id,
so don't need netif_rx skbagain
*/
goto err_unlock;
}
#endif
//netif_rx函数会让数据包重新进入__netif_receive_skb函数处理
netif_rx(skb);
rcu_read_unlock();
return NET_RX_SUCCESS;
err_unlock:
rcu_read_unlock();
err_free:
kfree_skb(skb);
return NET_RX_DROP;
}
数据包进来后先修改为对应的vlan_id,然后直接通过dev_queue_xmit函数把数据包发出去,仅对对应的VLAN包进行转发,其他数据包按照路由器的处理机制处理。
通过以上处理,实现VLAN数据的分流。
本文发布于:2024-02-01 13:41:42,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170676610237006.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |