linux网桥---接收

简介:       关于桥的知识,网上有很多文章,网桥从开始的硬件设备,到现在linux内核的软实现,包括后来的vlan,switch芯片.这里也是参考了《深入理解linux网络内幕》中桥的部分.      参考内核2.6.32.61  kernel/net/bridge/*   这里先普及下知识: HUB:整个HUB就是一个冲突域,采用CSMA/CD机制检测和侦听,从一个端口进来的数据包不经分析就会被转发到其它所有端口发送出去,连在此HUB上的设备共享带宽,利用率低,效率低,有距离限制,任意一个时刻只能有2台计算机之间可以通信。
      关于桥的知识,网上有很多文章,网桥从开始的硬件设备,到现在linux内核的软实现,包括后来的vlan,switch芯片.这里也是参考了《深入理解linux网络内幕》中桥的部分.  
   参考内核2.6.32.61  kernel/net/bridge/*
  这里先普及下知识:

HUB:整个HUB就是一个冲突域,采用CSMA/CD机制检测和侦听,从一个端口进来的数据包不经分析就会被转发到其它所有端口发送出去,连在此HUB上的设备共享带宽,利用率低,效率低,有距离限制,任意一个时刻只能有2台计算机之间可以通信。

网桥:建立桥接表(MAC),根据MAC表来决定向哪个端口进行数据转发,每个端口为一个冲突域,每台设备将享用一个端口的带宽。

交换机:网桥和交换机都是一个广播域,每个端口都是一个冲突域,并形成MAC表来指导帧转发,不同之处:交换机端口数量多,可以划分VLAN来将整个广播域分割为多个广播域。

  其实最新接触桥的是,在测试网络桥接性能的时候,然后在代码dev.c中:
 

点击(此处)折叠或打开

  1. #ifdef CONFIG_NET_CLS_ACT
  2.     skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
  3.     if (!skb)
  4.         goto out;
  5. ncls:
  6. #endif

  7.     skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);
  8.     if (!skb)
  9.         goto out;
  10.     skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev);
  11.     if (!skb)
  12.         goto out;

  13.     type = skb->protocol;
  14.     list_for_each_entry_rcu(ptype,
  15.             &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
  16.         if (ptype->type == type &&
  17.          (ptype->dev == null_or_orig || ptype->dev == skb->dev ||
  18.          ptype->dev == orig_dev)) {
  19.             if (pt_prev)
  20.                 ret = deliver_skb(skb, pt_prev, orig_dev);
  21.             pt_prev = ptype;
  22.         }
  23.     }
这里截取了netif_recevice_skb函数部分代码. 桥的处理函数为 handle_bridge,当然更新的内核这里调用的接口有些变化(比如3.1 )

点击(此处)折叠或打开

  1. rx_handler = rcu_dereference(skb->dev->rx_handler);
我们来看看handle_bridge

点击(此处)折叠或打开

  1. #if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE)

  2. #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
  3. /* This hook is defined here for ATM LANE */
  4. int (*br_fdb_test_addr_hook)(struct net_device *dev,
  5.              unsigned char *addr) __read_mostly;
  6. EXPORT_SYMBOL_GPL(br_fdb_test_addr_hook);
  7. #endif

  8. /*
  9.  * If bridge module is loaded call bridging hook.
  10.  * returns NULL if packet was consumed.
  11.  */
  12. struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p,
  13.                     struct sk_buff *skb) __read_mostly;
  14. EXPORT_SYMBOL_GPL(br_handle_frame_hook);

  15. static inline struct sk_buff *handle_bridge(struct sk_buff *skb,
  16.                      struct packet_type **pt_prev, int *ret,
  17.                      struct net_device *orig_dev)
  18. {
  19.     struct net_bridge_port *port;

  20.     if (skb->pkt_type == PACKET_LOOPBACK ||
  21.      (port = rcu_dereference(skb->dev->br_port)) == NULL)
  22.         return skb;

  23.     if (*pt_prev) {
  24.         *ret = deliver_skb(skb, *pt_prev, orig_dev);
  25.         *pt_prev = NULL;
  26.     }

  27.     return br_handle_frame_hook(port, skb);
  28. }
  29. #else
  30. #define handle_bridge(skb, pt_prev, ret, orig_dev)    (skb)
  31. #endif
这里 CONFIG_BRIDGE   CONFIG_BRIDGE_MODULE必须定义,否则桥处理函数为空.
skb - > pkt_type这个是在驱动层来判断的,一般由eth.c中eth_type_trans来初始化. 这里如果是回环设备或者不是桥端口则返回。

点击(此处)折叠或打开

  1. /**
  2.  * eth_type_trans - determine the packet's protocol ID.
  3.  * @skb: received socket data
  4.  * @dev: receiving network device
  5.  *
  6.  * The rule here is that we
  7.  * assume 802.3 if the type field is short enough to be a length.
  8.  * This is normal practice and works for any 'now in use' protocol.
  9.  */
  10. __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
  11. {
  12.     struct ethhdr *eth;
  13.     unsigned char *rawp;

  14.     skb->dev = dev;
  15.     skb_reset_mac_header(skb);
  16.     skb_pull(skb, ETH_HLEN);
  17.     eth = eth_hdr(skb);

  18.     if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
  19.         if (!compare_ether_addr_64bits(eth->h_dest, dev->broadcast))
  20.             skb->pkt_type = PACKET_BROADCAST;
  21.         else
  22.             skb->pkt_type = PACKET_MULTICAST;
  23.     }

  24.     /*
  25.      * This ALLMULTI check should be redundant by 1.4
  26.      * so don't forget to remove it.
  27.      *
  28.      * Seems, you forgot to remove it. All silly devices
  29.      * seems to set IFF_PROMISC.
  30.      */

  31.     else if (1 /*dev->flags&IFF_PROMISC */ ) {
  32.         if (unlikely(compare_ether_addr_64bits(eth->h_dest, dev->dev_addr)))
  33.             skb->pkt_type = PACKET_OTHERHOST;
  34.     }

  35.     /*
  36.      * Some variants of DSA tagging don't have an ethertype field
  37.      * at all, so we check here whether one of those tagging
  38.      * variants has been configured on the receiving interface,
  39.      * and if so, set skb->protocol without looking at the packet.
  40.      */
  41.     if (netdev_uses_dsa_tags(dev))
  42.         return htons(ETH_P_DSA);
  43.     if (netdev_uses_trailer_tags(dev))
  44.         return htons(ETH_P_TRAILER);

  45.     if (ntohs(eth->h_proto) >= 1536)
  46.         return eth->h_proto;

  47.     rawp = skb->data;

  48.     /*
  49.      * This is a magic hack to spot IPX packets. Older Novell breaks
  50.      * the protocol design and runs IPX over 802.3 without an 802.2 LLC
  51.      * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
  52.      * won't work for fault tolerant netware but does for the rest.
  53.      */
  54.     if (*(unsigned short *)rawp == 0xFFFF)
  55.         return htons(ETH_P_802_3);

  56.     /*
  57.      * Real 802.2 LLC
  58.      */
  59.     return htons(ETH_P_802_2);
  60. }
而pt_prev默认为空,它是嗅探器的处理.前面我们讲过.最近就是调用br_handle_frame_hook它了。
在br.c中 br_init函数中对它进行了初始化

点击(此处)折叠或打开

  1. br_handle_frame_hook = br_handle_frame;

点击(此处)折叠或打开

  1. /*
  2.  * Called via br_handle_frame_hook.
  3.  * Return NULL if skb is handled
  4.  * note: already called with rcu_read_lock (preempt_disabled)
  5.  */
  6. struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb)
  7. {
  8.     const unsigned char *dest = eth_hdr(skb)->h_dest;
  9.     int (*rhook)(struct sk_buff *skb);

  10.     if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) //检查源地址的合法性,非0、非全F,非多播地址. 这个函数可以展开. 
  11.         goto drop;

  12.     skb = skb_share_check(skb, GFP_ATOMIC);
  13.     if (!skb)
  14.         return NULL;

  15.     if (unlikely(is_link_local(dest))) {  // 判断目的地址是不是多播地址01:80:c2:00:00:0X,如果是进入处理 ;01:80:c2:00:00:00,由802.1D stp所使用.
  16.         /* Pause frames shouldn't be passed up by driver anyway */
  17.         if (skb->protocol == htons(ETH_P_PAUSE))
  18.             goto drop;

  19.         /* If STP is turned off, then forward */
  20.         if (p->br->stp_enabled == BR_NO_STP && dest[5] == 0)  //桥默认初始化的stp默认没有开启.,并且是stp地址. 那么直接转发.
  21.             goto forward;

  22.         if (NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, // 这里不关注netfilter(ebtables),br_handle_local_finish调用br_fdb_update更新报文的源mac与端口的关系(fdb),也是mac-port对应模式的更新. 它固定返回0值,所以除非有钩子过滤使返回值为真,返回null,否则返回skb.
  23.              NULL, br_handle_local_finish))
  24.             return NULL;    /* frame consumed by filter */
  25.         else
  26.             return skb;    /* continue processing */
  27.     }

  28. forward:
  29.     switch (p->state) {    // 根据桥端口状态不同处理.
  30.     case BR_STATE_FORWARDING:
  31.         rhook = rcu_dereference(br_should_route_hook); // 这里关于br_should_route_hook是ebtables的东西。在ebtables_broute.c中初始化的.当然需要开启它.转发后,返回skb.
  32.                                                     

    点击(此处)折叠或打开

    1. static int __init ebtable_broute_init(void)
    2. {
    3.     int ret;

    4.     ret = register_pernet_subsys(&broute_net_ops);
    5.     if (ret 0)
    6.         return ret;
    7.     /* see br_input.c */
    8.     rcu_assign_pointer(br_should_route_hook, ebt_broute);
    9.     return 0;
    10. }


  33.         if (rhook != NULL) {
  34.             if (rhook(skb))
  35.                 return skb;
  36.             dest = eth_hdr(skb)->h_dest;
  37.         }
  38.         /* fall through */
  39.     case BR_STATE_LEARNING:       //主要关注learning状态的处理.
  40.         if (!compare_ether_addr(p->br->dev->dev_addr, dest))   //判断目的mac是否发往本地的.如果是则设置pkt_type为PACKET_HOST
  41.             skb->pkt_type = PACKET_HOST;

  42.         NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
  43.             br_handle_frame_finish);                 //它需要单独分析.         
  44.         break;
  45.     default:
  46. drop:
  47.         kfree_skb(skb);
  48.     }
  49.     return NULL;
  50. }
这里在br_input.c定义. 我们首先看参数p,它来自handle_bridge 中 port 而port来自 port = rcu_dereference(skb->dev->br_port; 而这里的操作涉及网桥的添加,以及添加接口.
上面代码进行了简单的注释和分析.最后看 br_handle_frame_finish

点击(此处)折叠或打开

  1. /* note: already called with rcu_read_lock (preempt_disabled) */
  2. int br_handle_frame_finish(struct sk_buff *skb)
  3. {
  4.     const unsigned char *dest = eth_hdr(skb)->h_dest;
  5.     struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
  6.     struct net_bridge *br;
  7.     struct net_bridge_fdb_entry *dst;
  8.     struct sk_buff *skb2;

  9.     if (!p || p->state == BR_STATE_DISABLED)
  10.         goto drop;

  11.     /* insert into forwarding database after filtering to avoid spoofing */
  12.     br = p->br;
  13.     br_fdb_update(br, p, eth_hdr(skb)->h_source);   // 更新fdb

  14.     if (p->state == BR_STATE_LEARNING)
  15.         goto drop;

  16.     /* The packet skb2 goes to the local host (NULL to skip). */
  17.     skb2 = NULL;

  18.     if (br->dev->flags & IFF_PROMISC)
  19.         skb2 = skb;

  20.     dst = NULL;

  21.     if (is_multicast_ether_addr(dest)) {   //多播
  22.         br->dev->stats.multicast++;
  23.         skb2 = skb;
  24.     } else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) {  //本地
  25.         skb2 = skb;
  26.         /* Do not forward the packet since it's local. */
  27.         skb = NULL;
  28.     }

  29.     if (skb2 == skb)
  30.         skb2 = skb_clone(skb, GFP_ATOMIC);

  31.     if (skb2)
  32.         br_pass_frame_up(br, skb2);  // 发往本地的处理,把设备接口赋值为桥接口,调用netif_recevice_skb继续处理.

  33.     if (skb) {
  34.         if (dst)
  35.             br_forward(dst->dst, skb);  // 查询fdb找到mac-port信息,转发,通过br_forward_finish调用br_dev_queue_push_xmit发送出去.最后调用了dev_queue_xmit.
  36.         else
  37.             br_flood_forward(br, skb);  //没有查询到,广播,即循环桥每个端口,br_flood,用br_forward发送出去.
  38.     }

  39. out:
  40.     return 0;
  41. drop:
  42.     kfree_skb(skb);
  43.     goto out;
  44. }
上面讲了这么多,只是针对接收到的数据帧的桥处理。这里我们没有发现stp的处理。现在内核貌似已经把它剥离到其他模块了.










  
   
相关文章
|
存储 缓存 网络协议
深入理解Linux网络——内核是如何接收到网络包的
一、相关实际问题 RingBuffer是什么,为什么会丢包 网络相关的硬中断、软中断是什么 Linux里的ksoftirqd内核线程是干什么
|
5月前
|
Linux
Linux中openvswitch配置网桥详解
Linux中openvswitch配置网桥详解
166 0
|
存储 监控 Cloud Native
剖析Linux网络包接收过程:掌握数据如何被捕获和分发的全过程(上)
剖析Linux网络包接收过程:掌握数据如何被捕获和分发的全过程
|
6月前
|
监控 Shell Linux
【Shell 命令集合 网络通讯 】Linux 发送和接收传真 efax命令 使用指南
【Shell 命令集合 网络通讯 】Linux 发送和接收传真 efax命令 使用指南
86 0
|
6月前
|
存储 缓存 网络协议
深入理解Linux网络——内核是如何接收到网络包的
一、相关实际问题 RingBuffer是什么,为什么会丢包 网络相关的硬中断、软中断是什么 Linux里的ksoftirqd内核线程是干什么的 为什么网卡开启多队列能提升网络性能 tcpdump是如何工作的 iptable/netfilter是在哪一层实现的 tcpdump能否抓到被iptable封禁的包 网络接收过程中如何查看CPU开销 DPDK是什么
|
存储 网络协议 Linux
剖析Linux网络包接收过程:掌握数据如何被捕获和分发的全过程(下)
剖析Linux网络包接收过程:掌握数据如何被捕获和分发的全过程
|
Linux Docker 容器
宿主机上docker0 Linux 网桥设备是怎么来的?
在默认安装情况下, Docker 会在宿主机上创建一个名为 docker0 的 Linux 网桥设备。该网桥设备拥有一个私有网络地址及其所属子网。
134 0
|
弹性计算 Kubernetes 网络协议
k8s网络诊断之被丢弃的SYN--linux数据包的接收过程(k8s+flannel+ ipvs)
某客户反馈,ECS上自建nginx server 通过proxy_pass 反向代理 云上k8s集群 nodeport类型的svc,存在大量1s的延迟请求的问题,在nginx所在的ecs上,使用netstat可以看到syn_sent状态的connection,如下图所示,但是在pod所在的worker节点上是看不到syn_RECV状态的connection(nodeport上也无)
1299 0
|
网络协议 Linux