linux网桥处理函数学习------br_handle_frame_finish
2017-10-20 17:10
2201 查看
/* note: already called with rcu_read_lock */ int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb) { const unsigned char *dest = eth_hdr(skb)->h_dest; struct net_bridge_port *p = br_port_get_rcu(skb->dev); struct net_bridge *br; struct net_bridge_fdb_entry *dst; struct net_bridge_mdb_entry *mdst; struct sk_buff *skb2; bool unicast = true; u16 vid = 0; /*如果桥接口不存在,或者stp状态为disable,丢弃报文*/ if (!p || p->state == BR_STATE_DISABLED) goto drop; /*对vlan做相关判断,默认直接返回true*/ if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb, &vid)) goto out; #if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) #if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) if (skb->vlan_count) vid = (skb->vlan_header[0] >> 16) & VLAN_VID_MASK; else #endif /* CONFIG_BCM_VLAN) */ /* * dev.c/__netif_receive_skb(): if proto == ETH_P_8021Q * call vlan_untag() to remove tag and save vid in skb->vlan_tci */ /*从skb中获取vlan id*/ #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) if (vlan_tx_tag_present(skb)) #else if (skb_vlan_tag_present(skb)) #endif vid = skb->vlan_tci & VLAN_VID_MASK; else if ( vlan_eth_hdr(skb)->h_vlan_proto == htons(ETH_P_8021Q) ) vid = ntohs(vlan_eth_hdr(skb)->h_vlan_TCI) & VLAN_VID_MASK; #endif /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; /*获取网桥结构*/ if (p->flags & BR_LEARNING) /*如果网桥使能了mac学习的功能,则根据源mac地址更新mac转发表*/ br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false); /*组播报文的igmp处理*/ if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) && br_multicast_rcv(br, p, skb, vid)) goto drop; /*更新完mac转发表,如果端口还是处理stp学习状态,这个报文就此drop掉*/ #if defined(CONFIG_BCM_KF_WL) if ((p->state == BR_STATE_LEARNING) && (skb->protocol != htons(0x886c) /*ETHER_TYPE_BRCM*/) && (skb->protocol != htons(0x888e) /*ETHER_TYPE_802_1X*/) && (skb->protocol != htons(0x88c7) /*ETHER_TYPE_802_1X_PREAUTH*/)) #else if (p->state == BR_STATE_LEARNING) #endif goto drop; /*在skb中记录网桥设备*/ BR_INPUT_SKB_CB(skb)->brdev = br->dev; /* The packet skb2 goes to the local host (NULL to skip). */ skb2 = NULL; /*判断网卡是否处于混杂模式*/ if (br->dev->flags & IFF_PROMISC) skb2 = skb; dst = NULL; /*处理arp代理*/ if (IS_ENABLED(CONFIG_INET) && skb->protocol == htons(ETH_P_ARP)) br_do_proxy_arp(skb, br, vid, p); #if (defined(CONFIG_BCM_MCAST) || defined(CONFIG_BCM_MCAST_MODULE)) && defined(CONFIG_BCM_KF_MCAST) if ( br_bcm_mcast_receive != NULL ) { int rv = br_bcm_mcast_receive(br->dev->ifindex, skb, 0); if ( rv < 0 ) { /* there was an error with the packet */ goto drop; } else if ( rv > 0 ) { /* the packet was consumed */ goto out; } /* continue */ } #endif /*广播报文*/ if (is_broadcast_ether_addr(dest)) { #if defined(CONFIG_BCM_KF_EXTSTATS) { br->dev->stats.rx_broadcast_packets++; #endif skb2 = skb; #if defined(CONFIG_BCM_KF_EXTSTATS) } #endif unicast = false; } else if (is_multicast_ether_addr(dest)) { mdst = br_mdb_get(br, skb, vid); if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && br_multicast_querier_exists(br, eth_hdr(skb))) { if ((mdst && mdst->mglist) || br_multicast_is_router(br)) skb2 = skb; br_multicast_forward(mdst, skb, skb2); skb = NULL; if (!skb2) goto out; } else skb2 = skb; unicast = false; br->dev->stats.multicast++; #if defined(CONFIG_BCM_KF_EXTSTATS) br->dev->stats.rx_multicast_bytes += skb2->len; #endif #if !(defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG)) } else if ((dst = __br_fdb_get(br, dest, vid)) && dst->is_local) { skb2 = skb; /* Do not forward the packet since it's local. */ skb = NULL; } #else } else { struct net_bridge_fdb_entry *src; dst = __br_fdb_get(br, dest, vid); src = __br_fdb_get(br, eth_hdr(skb)->h_source, vid); blog_lock(); if (src) blog_link(BRIDGEFDB, blog_ptr(skb), (void*)src, BLOG_PARAM1_SRCFDB, 0); if (dst) blog_link(BRIDGEFDB, blog_ptr(skb), (void*)dst, BLOG_PARAM1_DSTFDB, 0); blog_unlock(); ...... ...... /*如果在mac转发表中找到表项,并且是本地的,设置skb2=skb,skb = NULL*/ if ((dst != NULL) && dst->is_local) { skb2 = skb; /* Do not forward the packet since it's local. */ skb = NULL; } } #endif /*1. fdb 表中存在表项且是本地端口或者组播处理完成,设置 skb2 = skb, skb = null, unicast = false, dst != null 2. 广播或者组播未处理完成 skb2 = skb, skb != null, unicast = false, dst = null 3. fdb表中不存在表项(未知单播),skb2 = null, skb != null, unicast = true, dst = null 4. fdb表中找到表项但不是本地端口, skb2 = null, skb != null, unicast = true, dst != null*/ if (skb) { if (dst) { /*转发表中存在并且不是本地的,即需要转发到其它端口*/ dst->used = jiffies; br_forward(dst->dst, skb, skb2); } else #if defined(CONFIG_BCM_KF_FBOND) && (defined(CONFIG_BCM_FBOND) || defined(CONFIG_BCM_FBOND_MODULE)) if (BR_STATE_BLOCKING == p->state) /* prevent flooding unknown unicast from blocked port */ goto drop; else #endif /*转发表中没有找到的单播(未知单播)或者广播,或者组播*/ br_flood_forward(br, skb, skb2, unicast); } /*设置了skb2表明是需要上送到自身的报文,如 1)找到转发表项并且目的是local 2)广播 3)组播*/ if (skb2) return br_pass_frame_up(skb2); out: return 0; drop: kfree_skb(skb); goto out; } EXPORT_SYMBOL_GPL(br_handle_frame_finish); /*广播,组播,fdb表项中指明目的是自己的报文,重新走netif_receive_skb_sk,根据协议号上送三层处理*/ 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 pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats); struct net_port_vlans *pv; #if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) blog_lock(); blog_link(IF_DEVICE, blog_ptr(skb), (void*)br->dev, DIR_RX, skb->len); blog_unlock(); /* Gather general RX statistics */ brdev->stats.rx_packets++; brdev->stats.rx_bytes += skb->len; #endif u64_stats_update_begin(&brstats->syncp); brstats->rx_packets++; brstats->rx_bytes += skb->len; u64_stats_update_end(&brstats->syncp); /* Bridge is just like any other port. Make sure the * packet is allowed except in promisc modue when someone * may be running packet capture. */ pv = br_get_vlan_info(br); if (!(brdev->flags & IFF_PROMISC) && !br_allowed_egress(br, pv, skb)) { kfree_skb(skb); return NET_RX_DROP; } indev = skb->dev; /*将skb->dev更新为brdev,即网桥设备,而不是实际物理口,这样子,重新走netif_receive_skb_sk时rx_handle就不会有值了*/ skb->dev = brdev; skb = br_handle_vlan(br, pv, skb); if (!skb) return NET_RX_DROP; /*修改接收设备后,重新走netif_receive_skb_sk流程*/ return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, NULL, skb, indev, NULL, netif_receive_skb_sk); } /*转发报文*/ /* called with rcu_read_lock */ void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0) { #if defined(CONFIG_BCM_KF_FBOND) && (defined(CONFIG_BCM_FBOND) || defined(CONFIG_BCM_FBOND_MODULE)) to = br_fb_process(to, skb); if ( to ) { #else if (should_deliver(to, skb)) { #endif if (skb0) deliver_clone(to, skb, __br_forward); else __br_forward(to, skb); return; } if (!skb0) kfree_skb(skb); } /*被br_forward转发报文*/ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) { struct net_device *indev; if (skb_warn_if_lro(skb)) { kfree_skb(skb); return; } skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb); if (!skb) return; indev = skb->dev; skb->dev = to->dev; /*修改skb->dev为转发表中的目的设备,实际的物理网口设备*/ skb_forward_csum(skb); /*调用br_forward_finish发送报文*/ NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, NULL, skb, indev, skb->dev, br_forward_finish); } /*br_forward_finish调用br_dev_queue_push_xmit*/ int br_forward_finish(struct sock *sk, struct sk_buff *skb) { return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, sk, skb, NULL, skb->dev, br_dev_queue_push_xmit); } /*br_dev_queue_push_xmit又调用dev_queue_xmit*/ int br_dev_queue_push_xmit(struct sock *sk, struct sk_buff *skb) { if (!is_skb_forwardable(skb->dev, skb)) { kfree_skb(skb); } else { skb_push(skb, ETH_HLEN); br_drop_fake_rtable(skb); skb_sender_cpu_clear(skb); dev_queue_xmit(skb); } return 0; } EXPORT_SYMBOL_GPL(br_dev_queue_push_xmit); ---------------------------------------------------------- 广播 ---------------------------------------------------------- /* called under bridge lock */ void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, struct sk_buff *skb2, bool unicast) { br_flood(br, skb, skb2, __br_forward, unicast); } /*__packet_hook就是br_flood_forward里面的__br_forward*/ /* called under bridge lock */ static void br_flood(struct net_bridge *br, struct sk_buff *skb, struct sk_buff *skb0, void (*__packet_hook)(const struct net_bridge_port *p, struct sk_buff *skb), bool unicast) { struct net_bridge_port *p; struct net_bridge_port *prev; #if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) Blog_t * blog_p = blog_ptr(skb); if (blog_p && !blog_p->rx.multicast) blog_skip(skb); #endif prev = NULL; /*循环网桥br下面的port_list链,调用__br_forward,在可以发送的端口上修改skb->dev,发送报文*/ list_for_each_entry_rcu(p, &br->port_list, list) { /* Do not flood unicast traffic to ports that turn it off */ if (unicast && !(p->flags & BR_FLOOD)) continue; /* Do not flood to ports that enable proxy ARP */ if (p->flags & BR_PROXYARP) continue; if ((p->flags & BR_PROXYARP_WIFI) && BR_INPUT_SKB_CB(skb)->proxyarp_replied) continue; prev = maybe_deliver(prev, p, skb, __packet_hook); if (IS_ERR(prev)) goto out; } if (!prev) goto out; if (skb0) deliver_clone(prev, skb, __packet_hook); else __packet_hook(prev, skb); return; out: if (!skb0) kfree_skb(skb); } static struct net_bridge_port *maybe_deliver( struct net_bridge_port *prev, struct net_bridge_port *p, struct sk_buff *skb, void (*__packet_hook)(const struct net_bridge_port *p, struct sk_buff *skb)) { int err; #if defined(CONFIG_BCM_KF_FBOND) && (defined(CONFIG_BCM_FBOND) || defined(CONFIG_BCM_FBOND_MODULE)) if (!should_deliver(p, skb, p->state)) #else if (!should_deliver(p, skb)) #endif return prev; if (!prev) goto out; err = deliver_clone(prev, skb, __packet_hook); if (err) return ERR_PTR(err); out: return p; } static int deliver_clone(const struct net_bridge_port *prev, struct sk_buff *skb, void (*__packet_hook)(const struct net_bridge_port *p, struct sk_buff *skb)) { struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; #if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) struct sk_buff *skb2 = skb; #endif skb = skb_clone(skb, GFP_ATOMIC); if (!skb) { dev->stats.tx_dropped++; return -ENOMEM; } #if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) blog_clone(skb2, blog_ptr(skb)); #endif __packet_hook(prev, skb); return 0; }
相关文章推荐
- linux网桥处理函数学习-----br_handle_frame
- 桥数据包处理函数——br_handle_frame_finish(七)
- Linux C学习笔记 —— 字符串处理函数(string.h)
- br_handle_frame()函数
- Linux邻居协议 学习笔记 之二 通用邻居处理函数对应的数据结构的分析
- Linux 多线程应用中如何编写安全的信号处理函数
- MyGeneration学习笔记(8) :dOOdad提供的数据绑定、特殊函数和事务处理
- 学习用于异常处理的terminate()函数
- Linux下的信号处理函数
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之三--Linux对异常和中断的处理
- linux C 常用文件处理函数
- 【嵌入式Linux学习七步曲之第七篇 Linux的高级应用编程】TCP/IP网络编程函数解析
- linux的文本处理(学习笔记)
- 2.6版本Linux上替换系统调用函数实现隐藏文件学习
- 学习用于异常处理的terminate()函数
- 通过unregister_filesystem()函数学习linux单链表操作
- Linux 多线程应用中如何编写安全的信号处理函数
- Linux下C语言编程--信号处理函数
- MyGeneration学习笔记(8) :dOOdad提供的数据绑定、特殊函数和事务处理
- php字符串处理函数大全(学习)