这节,简单看一下,数据包的转发过程:
无论是在发往本地还是转发,有一个函数的功能是不能忽略的,就是br_handle_vlan函数
struct sk_buff *br_handle_vlan(struct net_bridge *br, struct net_bridge_vlan_group *vg, struct sk_buff *skb) { struct br_vlan_stats *stats; struct net_bridge_vlan *v; u16 vid; /* If this packet was not filtered at input, let it pass */ if (!BR_INPUT_SKB_CB(skb)->vlan_filtered) goto out; /* At this point, we know that the frame was filtered and contains * a valid vlan id. If the vlan id has untagged flag set, * send untagged; otherwise, send tagged. */ br_vlan_get_tag(skb, &vid); /*find vid from vlan group*/ v = br_vlan_find(vg, vid); /* Vlan entry must be configured at this point. The * only exception is the bridge is set in promisc mode and the * packet is destined for the bridge device. In this case * pass the packet as is. */ if (!v || !br_vlan_should_use(v)) { if ((br->dev->flags & IFF_PROMISC) && skb->dev == br->dev) { goto out; } else { kfree_skb(skb); return NULL; } } /*statistacs the vlan if flow and if the vlan_stats_enabled is true */ if (br->vlan_stats_enabled) { stats = this_cpu_ptr(v->stats); u64_stats_update_begin(&stats->syncp); stats->tx_bytes += skb->len; stats->tx_packets++; u64_stats_update_end(&stats->syncp); } if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED) skb->vlan_tci = 0; out: return skb; } 这个函数的作用很简单就是,数据包是否要带tag, 过程: 在传递进来的vlan group中查找自己所处的vlan 如果该vlan不存在则判断当前模式是否是混杂模式和数据包的设备是否是桥下的设备,选择发包或者丢弃。 如果存在,且vlan是开启的,则统计vlan接口上的数据流量,最后根据vlan出口的标记位进行位运算判断是否要带tag.然后我们来看一下上节提到的发往本地数据包的处理函数
static int br_pass_frame_up(struct sk_buff *skb) { struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; struct net_bridge *br = netdev_priv(brdev); struct net_bridge_vlan_group *vg; struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats); /*统计该桥上的流量*/ u64_stats_update_begin(&brstats->syncp); brstats->rx_packets++; brstats->rx_bytes += skb->len; u64_stats_update_end(&brstats->syncp); /*获取该桥上的vlan组*/ vg = br_vlan_group_rcu(br); /* Bridge is just like any other port. Make sure the * packet is allowed except in promisc modue when someone * may be running packet capture. */ if (!(brdev->flags & IFF_PROMISC) && !br_allowed_egress(vg, skb)) { kfree_skb(skb); return NET_RX_DROP; } /*替换掉数据包中的设备信息改为桥设备*/ indev = skb->dev; skb->dev = brdev; /*配置数据包vlan的相关信息*/ skb = br_handle_vlan(br, vg, skb); if (!skb) return NET_RX_DROP; /*进入NF_BR_LOCAL_IN*/ return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, dev_net(indev), NULL, skb, indev, NULL, br_netif_receive_skb); } 这个函数所做的事情很简单,就是配置vlan的相关信息后,然后发往本地的netfilter钩子函数中 最后重新回到netif_recive_skb.如下函数: static int br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb) { return netif_receive_skb(skb); }再来看看数据包转发的函数 static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) { struct net_bridge_vlan_group *vg; struct net_device *indev; if (skb_warn_if_lro(skb)) { kfree_skb(skb); return; } /*获取vlan组,这个组中有许多的vlanid,br_handle_vlan函数就是要在这个组中查找自己的vid*/ vg = nbp_vlan_group_rcu(to); /*添加vlan的相关配置*/ skb = br_handle_vlan(to->br, vg, skb); if (!skb) return; indev = skb->dev; skb->dev = to->dev; skb_forward_csum(skb); NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, dev_net(indev), NULL, skb, indev, skb->dev, br_forward_finish); } int br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, net, sk, skb, NULL, skb->dev, br_dev_queue_push_xmit); } 整个数据包转发的过程与转发到本地的过程类似,只不过所进入的netfilter钩子点不同. 整个分析中不包含数据包从本地发出的数据包