对于网桥端口设备,底层接收到数据以后,经过网卡驱动的接收函数处理以后,最终会调用函数netif_receive_skb,而netif_receive_skb在对数据包头进行相关处理以及对ptype_all链上注册的相关协议进行调用deliver_skb处理后(包括PF_PACKET类型的rawsocket处理),会调用handle_bridge进入网桥处理,而其会调用br_handle_frame_hook,即会调用br_handle_frame。

 

函数调用流程为netif_receive_skb->handle_bridge->br_handle_frame.

 

下面分析函数br_handle_frame,其作用是处理网桥入口数据主要函数。

 

1、判断mac地址是否有效(既不是组播mac地址也不是0mac地址)

 

2、判断skb包是否有效数据包

 

3、判断目的mac地址是否是01 80 c2 00 0x类型,若是,则继续判断是0x8808 协议,若是0x8808,个人理解则可能是mpcp相关

      的数据包,而mpcp是epon相关的协议,而mpcp协议中相关的消息和epon mac的硬件息息相关,linux内核对这类数据包就

     没有提供相关的公共函数了。对于其他类型的   数据包,则调用函数br_handle_local_finish进行后续处理,而br_handle_local_finish

      也仅仅是调用br_fdb_update,更新fdb数据库


4、对于网桥端口是forward和learning状态的,则调用防火墙处理函数

       处理NF_BR_PRE_ROUTING的ebtables相关的规则。

5、当通过NF_BR_PRE_ROUTING相关的ebtables规则后,则会调用函数

      br_handle_frame_finish继续进行数据处理

struct sk_buff *br_handle_frame(struct net_bridge_port *p,struct sk_buff *skb)

{

    const unsigned char*dest = eth_hdr(skb)->h_dest;

    int (*rhook)(structsk_buff *skb);

 

    if(!is_valid_ether_addr(eth_hdr(skb)->h_source))

        goto drop;

 

    skb =skb_share_check(skb, GFP_ATOMIC);

    if (!skb)

        return NULL;

 

    if(unlikely(is_link_local(dest))) {

        /* Pause framesshouldn't be passed up by driver anyway */

        if(skb->protocol == htons(ETH_P_PAUSE))

            goto drop;

 

        /* If STP isturned off, then forward */

        if(p->br->stp_enabled == BR_NO_STP && dest[5] == 0)

            goto forward;

 

        if(NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,

                NULL, br_handle_local_finish))

            return NULL;    /* frame consumed by filter */

        else

            return skb; /* continue processing */

    }

 

forward:

    switch (p->state) {

    caseBR_STATE_FORWARDING:

        rhook =rcu_dereference(br_should_route_hook);

        if (rhook != NULL){

            if (rhook(skb))

                returnskb;

            dest =eth_hdr(skb)->h_dest;

        }

        /* fall through */

    caseBR_STATE_LEARNING:

        if(!compare_ether_addr(p->br->dev->dev_addr, dest))

            skb->pkt_type= PACKET_HOST;

 

        NF_HOOK(PF_BRIDGE,NF_BR_PRE_ROUTING, skb, skb->dev, NULL,

            br_handle_frame_finish);

        break;

    default:

drop:

        kfree_skb(skb);

    }

    return NULL;

}

 

 

在这个函数的尾部,会调用br_handle_frame_finish,进行后续的处理,我们继续分析函数br_handle_frame_finish。该函数的作用是决定数据包的走向:转发、扩散或是丢弃

下面分析这个函数的主要流程:

1、首先判断接收到数据包的设备对应的网桥端口的状态是否为disable

2、调用br_fdb_update,更新fdb数据库,为数据包源mac地址与源网桥端口

       添加fdb entry,关于br_fdb_update,请参考我前面的分析文档

3、如果源网桥端口的   状态为learning,则不处理该数据包

4、a)如果网桥设备处于混杂模式或者数据包的目的mac地址为组播

          地址,则需要将该skb的一个拷贝,发送给上层协议栈(通过调用

         br_pass_frame_up实现)

       b)如果数据包的目的mac地址为本地mac,则只只需要将该数据包

          发送给上次协议栈,而不需转发数据包。

5、调用__br_fdb_get查找符合条件的fdb entry,

       a)若查找到了了符合条件的fdbentr

       i)若该fdb entry为local类型的,说明该数据包是发往本地的,则将

         skb赋值给skb2,然后skb指向NULL,不对该数据包进行转发。

       ii)若该fdb entry不是local类型的,则调用br_forward,将数据包从指定端口转发

               出去。

    b)若没有查找到指定的端口,则调用br_flood_forward,将数据从其他所有

       网桥端口发送出去         


int br_handle_frame_finish(struct sk_buff *skb)

{

    const unsigned char*dest = eth_hdr(skb)->h_dest;

    struct net_bridge_port*p = rcu_dereference(skb->dev->br_port);

    struct net_bridge *br;

    structnet_bridge_fdb_entry *dst;

    struct sk_buff *skb2;

 

    if (!p || p->state== BR_STATE_DISABLED)

        goto drop;

 

    /* insert intoforwarding database after filtering to avoid spoofing */

    br = p->br;

    br_fdb_update(br, p,eth_hdr(skb)->h_source);

 

    if (p->state ==BR_STATE_LEARNING)

        goto drop;

 

    /* The packet skb2goes to the local host (NULL to skip). */

    skb2 = NULL;

 

    if(br->dev->flags & IFF_PROMISC)

        skb2 = skb;

 

    dst = NULL;

 

    if(is_multicast_ether_addr(dest)) {

        br->dev->stats.multicast++;

        skb2 = skb;

    } else if ((dst =__br_fdb_get(br, dest)) && dst->is_local) {

        skb2 = skb;

        /* Do not forwardthe packet since it's local. */

        skb = NULL;

    }

 

    if (skb2 == skb)

        skb2 =skb_clone(skb, GFP_ATOMIC);

 

    if (skb2)

        br_pass_frame_up(br,skb2);

 

    if (skb) {

        if (dst)

            br_forward(dst->dst,skb);

        else

            br_flood_forward(br,skb);

    }

 

out:

    return 0;

drop:

    kfree_skb(skb);

    goto out;

}

 

 

在这个函数里,主要的处理函数有br_pass_frame_up、br_forward、br_flood_forward,而br_forward、br_flood_forward是转发相关的函数,我们下节进行分析。

我们现在分析下函数br_pass_frame_up

该函数的作用是将数据包传递给上层协议栈进行处理。该函数的处理流程如下:

1、将skb->dev设置为brdev

2、调用NF_HOOK处理input链上的防火墙规则,对于准许通过的

       数据包,则调用netif_receive_skb进行后续处理,此时由于skb->dev

       为brdev,所以这次netif_receive_skb调用则会跳过brdige_handle,上传给

       上层协议栈进行后续处理。

static void br_pass_frame_up(struct net_bridge *br, structsk_buff *skb)

{

    struct net_device*indev, *brdev = br->dev;

 

    brdev->stats.rx_packets++;

    brdev->stats.rx_bytes+= skb->len;

 

    indev = skb->dev;

    skb->dev = brdev;

 

    NF_HOOK(PF_BRIDGE,NF_BR_LOCAL_IN, skb, indev, NULL,

        netif_receive_skb);

}

 

 

 

 

至此,入口数据处理函数分析完毕,主要是br_input.c中的函数。

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐