// 网桥设备驱动程序的hard_start_xmit函数
// 函数主要任务:
// 1.广播或多播地址,在所有端口上扩散
// 2.存在转发项,在指定端口上发送
// 3.没有找到转发项,在所有端口上扩散
1.1 int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
const unsigned char *dest = skb->data;
struct net_bridge_fdb_entry *dst;
//更新网桥设备的统计信息
br->statistics.tx_packets++;
br->statistics.tx_bytes += skb->len;
//更新l2头位置
skb->mac.raw = skb->data;
skb_pull(skb, ETH_HLEN);
rcu_read_lock();
if (dest[0] & 1)//l2地址的第一个字节的第一位为1,则为多播地址
br_flood_deliver(br, skb, 0);//在所有端口上发送此报文
else if ((dst = __br_fdb_get(br, dest)) != NULL)//如果转发数据中存在目标地址的记录
br_deliver(dst->dst, skb);//通过与目标地址相关的端口发送
else
br_flood_deliver(br, skb, 0);
rcu_read_unlock();
return 0;
}
// 在所有端口扩散skb
// 参数:__packet_hook, __br_deliver
// 调用路径:br_dev_xmit->br_flood_deliver->br_flood
1.3 static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
void (*__packet_hook)(const struct net_bridge_port *p,
struct sk_buff *skb))
{
struct net_bridge_port *p;
struct net_bridge_port *prev;
//指示是否复制一份skb,br_dev_xmit->br_flood_deliver->br_flood此调用路径clone=0
if (clone) {
struct sk_buff *skb2;
if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
br->statistics.tx_dropped++;
return;
}
skb = skb2;
}
prev = NULL;
//遍历所有的端口
list_for_each_entry_rcu(p, &br->port_list, list) {
if (should_deliver(p, skb)) {//skb的输出设备非此端口,并且此端口处于转发模式
if (prev != NULL) {//在第一次运行时,prev=NULL,直接复制prev=p
struct sk_buff *skb2;
if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {//克隆sk_buff,共享packet data
br->statistics.tx_dropped++;
kfree_skb(skb);
return;
}
__packet_hook(prev, skb2);//调用__br_deliver,执行一系列的netfilter调用点,最后由dev_queue_xmit完成传输
}
prev = p;
}
}
if (prev != NULL) {
__packet_hook(prev, skb);//对最后一个端口调用__br_deliver
return;
}
kfree_skb(skb);
}
// 在网桥端口传输
// 调用路径:br_flood->hook->br_dev_queue_push_xmit
1.6 int br_dev_queue_push_xmit(struct sk_buff *skb)
{
if (skb->len > skb->dev->mtu)//如果skb的长度大于出口设备的mtu,则直接丢弃封包
kfree_skb(skb);
else {
skb_push(skb, ETH_HLEN);
dev_queue_xmit(skb);//输出
}
return 0;
}
// 判断端口是否应该发送数据帧
// 端口发送数据帧的条件:
// 1.skb的入口设备非此端口
// 2.端口处于转发模式
1.7 static inline int should_deliver(const struct net_bridge_port *p,
const struct sk_buff *skb)
{
if (skb->dev == p->dev ||//skb的接收端口为此端口
p->state != BR_STATE_FORWARDING)//端口非转发模式
return 0;
return 1;
}
// 网桥入口数据帧处理
// 调用路径:netif_receive_skb->handle_bridge
//
// 由网桥接收入口数据的条件:
// 1. 入口设备非回环地址
// 2. 入口设备为网桥端口
2.1 static __inline__ int handle_bridge(struct sk_buff **pskb,
struct packet_type **pt_prev, int *ret)
{
struct net_bridge_port *port;
if ((*pskb)->pkt_type == PACKET_LOOPBACK ||
(port = rcu_dereference((*pskb)->dev->br_port)) == NULL)//如果设备为网桥的端口,则dev->br_port为网桥端口控制块
return 0;
if (*pt_prev) {
*ret = deliver_skb(*pskb, *pt_prev);
*pt_prev = NULL;
}
//此设备为网桥的端口
return br_handle_frame_hook(port, pskb);
}
// 网桥处理入口流量
// 参数:
// p,接收到此入口流量的端口
// pskb, 入口数据帧
//
// 返回值:
// 0,网桥没有处理此pskb,netif_receive_skb继续处理pskb
// 1,网桥已经处理此pskb,netif_receive_skb无需继续处理此pskb
//
// 调用路径 : netif_receive_skb->handle_bridge->br_handle_frame
//
// 函数主要任务:
// 1.检查分包合法性:
// 1.1 接收端口开启状态
// 1.2 源地址非多播或广播地址
// 2.向转发数据库添加转发项
// 3.识别配置bpdu,处理配置bpdu
// 4.决定入口流量被路由还是桥接
// 5.如果数据帧需要被桥接,则由桥接处理程序继续处理。
// 注:
// 1.只有转发态(BR_STATE_FORWARDING),学习态(BR_STATE_LEARNING)的网桥端口,可以向转发数据库添加转发项。
// 2.bpdu的识别办法:以太网帧的目的地址为[0x01, 0x80, 0xc2, 0x00, 0x00, 0x00]
// 3.决定入口流量被路由还是桥接,通过ebtable_broute决定
// 4.只有转发态(BR_STATE_FORWARDING)的网桥端口,可以处理普通入口流量。
2.2 int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
const unsigned char *dest = eth_hdr(skb)->h_dest;
if (p->state == BR_STATE_DISABLED)//端口被关闭
goto err;
if (eth_hdr(skb)->h_source[0] & 1)//源地址为广播地址,说明错误
goto err;
if (p->state == BR_STATE_LEARNING ||//如果当前端口处于学习状态,或者转发状态
p->state == BR_STATE_FORWARDING)
br_fdb_insert(p->br, p, eth_hdr(skb)->h_source, 0);//在转发数据库中添加一条新转发项,或者更新现有的转发项
if (p->br->stp_enabled &&//网桥的stp协议开启
!memcmp(dest, bridge_ula, 5) &&//网桥组播地址的前5个字节
!(dest[5] & 0xF0)) {//bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 },通过入口帧的目标地址识别bpdu数据帧
if (!dest[5]) {
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
NULL, br_stp_handle_bpdu);//入口bpdu的数据帧,可以被任何端口接收,只要这个端口没有通过管理性手段关闭
return 1;
}
}
else if (p->state == BR_STATE_FORWARDING) {//当前端口处于转发状态,一个封包是被路由还是桥接,通过br_should_route_hook函数决定
if (br_should_route_hook) {//在ebtable_broute模块中被设置
if (br_should_route_hook(pskb))
return 0;
skb = *pskb;
dest = eth_hdr(skb)->h_dest;
}
if (!memcmp(p->br->dev->dev_addr, dest, ETH_ALEN))//目标地址为本地
skb->pkt_type = PACKET_HOST;
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish);//默认情况下,被绑定的设备所接收到的网络流量会分配给它的指定网桥
return 1;
}
err:
kfree_skb(skb);
return 1;
}
// 桥接入口流量
// 调用路径: br_handle_frame->br_handle_frame_finish
// 函数主要任务:
// 1.网桥端口均处于混杂模式,向网桥本地接收模块传递一份此数据帧
// 2.多播或广播目的地址,在除接收端口外的所有端口,扩散此入口数据帧,退出
// 3.如果,数据帧目的地址为本机,向网桥本地接收模块传递一份此数据帧,退出
// 4.否则,如果网桥知道能到达目的地址的输出端口,从输出端口发送此数据帧,退出
// 5.否则,如果网桥不知道使用哪个输出端口,向所有端口扩散,退出
2.3 int br_handle_frame_finish(struct sk_buff *skb)
{
const unsigned char *dest = eth_hdr(skb)->h_dest;
struct net_bridge_port *p = skb->dev->br_port;
struct net_bridge *br = p->br;
struct net_bridge_fdb_entry *dst;
int passedup = 0;
if (br->dev->flags & IFF_PROMISC) {//设备处于混杂模式
struct sk_buff *skb2;
skb2 = skb_clone(skb, GFP_ATOMIC);//复制一份sk_buff,共享packet data
if (skb2 != NULL) {
passedup = 1;
br_pass_frame_up(br, skb2);//向上层协议传递skb
}
}
if (dest[0] & 1) {//以太网多播地址
br_flood_forward(br, skb, !passedup);//从各个端口扩散此skb
if (!passedup)
br_pass_frame_up(br, skb);//如果处于混杂模式,则已经向上传递了一份skb
goto out;
}
dst = __br_fdb_get(br, dest);//非多播地址的skb,查询转发数据库
if (dst != NULL && dst->is_local) {//本地地址
if (!passedup)
br_pass_frame_up(br, skb);//向上层协议传递一份skb,由于网桥端口为混杂模式,之前会传递一份,所以此处不会再传递
else
kfree_skb(skb);
goto out;
}
if (dst != NULL) {
br_forward(dst->dst, skb);//非本地地址,从通往目标地址的端口发送此skb
goto out;
}
br_flood_forward(br, skb, 0);//非以太网多播,没有明确的出口设备,从所有端口上扩散
out:
return 0;
}
// 网桥向本地传递流量
// 调用路径:br_handle_frame_finish->br_pass_frame_up
// 函数主要任务:
// 1.skb->dev设置为网桥设备,为skb重新走一遍netif_receive_skb
// 注:网桥端口处于混杂模式,所有数据帧均会向上传递,并且一个数据帧,只会向本机传递一次。
2.4 static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
{
struct net_device *indev;
//增加统计数据
br->statistics.rx_packets++;
br->statistics.rx_bytes += skb->len;
indev = skb->dev;
skb->dev = br->dev;//修改接收此skb的设备为网桥设备
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
br_pass_frame_up_finish);
}
// 网桥向本地传递流量
// 使用积压设备的方式
// 调用路径:br_pass_frame_up->br_pass_frame_up_finish
2.5 static int br_pass_frame_up_finish(struct sk_buff *skb)
{
//挂载积压设备到poll_list,skb到input_pkt_queueu, 触发接收软中断
netif_rx(skb);
return 0;
}