您的位置:首页 > 运维架构 > Linux

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网桥