码迷,mamicode.com
首页 > 其他好文 > 详细

CORE网络数据包接收传递过程分析

时间:2015-07-18 13:56:42      阅读:176      评论:0      收藏:0      [点我收藏+]

标签:

CORE网络数据包接收传递过程分析

 

能够接收实际网络流量是CORE的一个显著优点,这使得已有的系统能方便地接入虚拟网络进行模拟。CORE对网络设备的虚拟是通过LXC技术来实现的,而对网络的虚拟则是通过虚拟网卡(veth)、网桥(Bridge)、Quagga来实现的。本文档主要通过分析CORE中网络数据传递过程,来理解CORE网络模拟。

拓扑结构

为了方便描述,以如图1所示拓扑结构为例子,分析数据流从网卡eth0到虚拟节点n2的过程。

 

图1 示例拓扑

虚拟网络创建由CORE后台根据前台的拓扑结构和配置,执行相应的命令进行实现,如下:

#创建容器n1

/usr/sbin/vnoded -v -c /tmp/pycore.52385/n1 -l /tmp/pycore.52385/n1.log -p /tmp/pycore.52385/n1.pid -C /tmp/pycore.52385/n1.conf

#创建容器n2

/usr/sbin/vnoded -v -c /tmp/pycore.52385/n2 -l /tmp/pycore.52385/n2.log -p /tmp/pycore.52385/n2.pid -C /tmp/pycore.52385/n2.conf

 

#创建网桥br.38079.56309

/usr/sbin/brctl  addbr  b.38079.56309

#关闭spanning tree protocol and forwarding delay并启动网桥

/usr/sbin/brctl  stp    b.38079.56309  off

/usr/sbin/brctl  setfd  b.38079.56309   0

/sbin/ip link  set  b.38079.56309  up

#创建网络转发过滤器ebtables,规则为接收和转发

/sbin/ebtables  -N  b.38079.56309  -P  ACCEPT

/sbin/ebtables  -A  FORWARD  --logical- in  b.38079.56309 –j  b.38079.56309

#通过vcmd执行,关闭多播探测

echo‘0’> /sys/devices/virtual/net/b.38079.56311/bridge/multicast_snooping

 

#创建虚拟网卡n1.eth0.44

/sbin/ip  link  add  name  n1.eth0.44  type  veth  peer  name  n1.0.44

#将虚拟网卡加入到容器的命名空间中,并命名为eth0

/sbin/ip  link  set  n1.0.44  netns  17122     #17122为节点n1的vnoded进程的ID

/sbin/ip  link  set  n1.0.44  name  eth0

/sbin/ip  link  set  n1.eth0.44  up

 

#将虚拟网卡绑定到网桥上

/usr/sbin/brctl  addif   b. 38079.56309   n1.eth0.44

/sbin/ip  link  set  n1.eth0.44  up

#通过vcmd执行,设置网卡mac地址

/sbin/ip  link  set  dev  eth0  address  00:00:00:aa:00:00    #node内执行

/sbin/ip  addr  add  10.0.0.1/24  dev  eth0               #node内执行

/sbin/ip  link  set  eth0  up                                                           #node内执行

 

#通过vcmd执行,在容器n1启动路由服务

sh  quaggaboot.sh  zebra

sh  quaggaboot.sh  ospfd

sh  quaggaboot.sh  vtysh

不难发现示例拓扑内部结构为图2。

 

图2 示例拓扑内部结构

       在示例拓扑中,CORE创建了两个容器(命名空间),两个网桥,三个虚拟网卡对(其中一端放进了容器中),并在命名空间中虚拟路由层,为了接收数据,命名空间中的虚拟网卡被连接到网桥上,而网桥可以连接物理网卡或其它虚拟网卡。这样就实现了虚拟节点、物理网卡之间的互联。

数据流分析

       以示例拓扑结构为例,第一步是网卡接收数据包,在网卡硬件中断响应中完成。为了尽可能快速处理,网卡接收中断响应函数会将接收数据生成skb结构体,将通过调用netif_rx(skb)将任务以软中断的方式插入到CPU调度队列,然后立即返回。第二步,ksoftirqd处理软中断。这个过程,ksoftirqd根据软中断类型,调用不同的响应函数。本例中ksoftirqd将调用netif_receive_skb函数对skb包进行处理。netif_receive_skb可以理解为从物理层接收数据包,因此它也算是链路层的入口函数。在这个函数中,会调用到handle_bridge函数,将skb交给网卡进行处理(如果配置了网桥),调用br_handle_frame_hook。第三步,网桥会依据ebtables规则对skb作转发、广播、丢弃等操作。CORE中定义了ebtables规则为FORWARDING,因此网桥会将ebtables转发给连接到它的另一个(虚拟)网卡,调用veth_xmit。第四步,对于虚拟网卡而言,发送就是接收,于是在veth_xmit中,它将skb重新以软中断的方式插入到CPU调度队列,此时skb已在容器中运行。第五步,ksoftirqd调用netif_receive_skb函数,由于容器中没有配置网桥,netif_receive_skb函数会调用packet_type成员.func = ip_rcv将数据包送至L3层路由层。第六步,路由层从ip_rcv接收skb,调用ip_rcv_finish对skb进行路由(调用skb_rtable),决定本地接收还是转发skb。

 

图3 数据流接收转发过程

各组成部分

      

物理网卡eth0

       物理网卡,也叫以太网卡,在内核的表现形式为ethernet_device(简写为eth0)。它的实现是由网卡驱动提供的。网卡既是PCI设备,也是网络设备。它在开启时需要注册中断响应函数xxx_interrupt。

request_irq(dev->irq, &xxx_interrupt,

                     lp->shared_irq ? IRQF_SHARED : 0, dev->name,

                     (void *)dev)

       当接收数据包时,会触发网卡中断,网卡驱动会为新到来的数据包申请skb, 并将网卡中的数据读到skb, 并通过netif_rx发送到内核协议栈上层。

if (rtl8169_rx_vlan_skb(tp, desc, skb) < 0)

               netif_rx(skb);

        dev->stats.rx_bytes += pkt_size;

        dev->stats.rx_packets++;

这里有必要说一下skb,它用于存储网络数据包,结构如图所示。skb由网卡生成。由于它包含了net_device,协议类型,地址等。每一层在遇到skb时都能根据自己的业务逻辑进行处理。

 

图4 skb结构

网桥Bridge

       网桥(Bridge)是L2层(数据链路层)设备,用于实现以太网设备之间的桥接。网桥可以绑定若干个以太网接口设备(利用brctl addif命令),从而实现网卡之间的互联。

(1)    模块初始化

在Linux中,网桥由动态加载模块Bridge完成。模块初始化时,除了完成自身模块初始化外,最重要一点就是向系统注册网桥处理HOOK。该HOOK将在handle_bridgge中调用。

br_handle_frame_hook = br_handle_frame;

(2)    与网卡绑定

 

图4  net_bridge结构体

网桥与网卡绑定通过与网桥端口绑定来实现。如图4所示,net_bridge结构体中维护了一个net_bridge_port结构体链表,而net_device(包含在eth设备结构体中)有指向net_bridge_port结构体的指针,该指针可以指向net_bridge_port结构体链表中的元素。

(3)    桥接处理

桥接处理一方面是接收数据,另一方面是发送数据。本例中没有通过网桥来发送数据,在此不作讨论。接收数据从br_handle_frame开始。

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

 int (*rhook)(struct sk_buff *skb);

 //判断是否为有效的物理地址,非全0地址以及非广播地址

 if (!is_valid_ether_addr(eth_hdr(skb)->h_source))goto drop;

 //判断skb包是否被共享skb->users != 1,若是,则复制一份,否则直接返回

 skb = skb_share_check(skb, GFP_ATOMIC);

 if (!skb) return NULL;

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

 int (*rhook)(struct sk_buff *skb);

 //判断是否为有效的物理地址,非全0地址以及非广播地址

 if (!is_valid_ether_addr(eth_hdr(skb)->h_source))goto drop;

 //判断skb包是否被共享skb->users != 1,若是,则复制一份,否则直接返回

 skb = skb_share_check(skb, GFP_ATOMIC);

 if (!skb)  return NULL;

 //这个函数是在判断是否为链路本地多播地址,01:80:c2:00:00:0x

 if (unlikely(is_link_local(dest))) {

 /* Pause frames shouldn‘t be passed up by driver anyway */

 if (skb->protocol == htons(ETH_P_PAUSE))  goto drop;

 /* If STP is turned 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) {

  case BR_STATE_FORWARDING:

  //如果网桥处于forwarding状态,且该报文要走L3层进行转发,则直接返回

  //br_should_route_hook钩子函数在ebtable里面设置为ebt_broute函数,它根据用户的规决定该报文是否要通过L3层来转发;一般rhook为空

  rhook = rcu_dereference(br_should_route_hook);

  if (rhook != NULL) {

  if (rhook(skb))

  return skb;

  dest = eth_hdr(skb)->h_dest;

  }

  /* fall through */

  case BR_STATE_LEARNING:

  //如果数据包的目的mac地址为虚拟网桥设备的mac地址,则标记为host

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

         skb->pkt_type = PACKET_HOST;

  //调用网桥在NF_BR_PREROUTING处挂载的钩子函数,

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

  br_handle_frame_finish);

  break;

  default:

  drop:

  kfree_skb(skb);

  }

  return NULL;

  FORWARDING以及LEARNING为网桥的状态,网桥端口一般有5种状态:

  1)  disable 被管理员禁用

  2)  blcok 休息,不参与数据包转发

  3)  listening 监听

  4)  learning 学习ARP信息,准备向工作状态改变

  5)  forwarding 正常工作,转发数据包

(4)    网桥过滤器ebtables_filter

网桥提供可配置的过滤转发规则,这一功能通过ebtables_filter来实现。ebtables_filter,ebtables和xt_tables都是可装载模块。ebtables用于存储过滤规则,每条过滤规则都对应ebtables中一个表项。如图5所示,在ebtables_filter模块初始化时向Bridge注册了各类HOOK函数。Bridge在接受、发送、转发数据包时调用相应的HOOK函数,实现过滤功能。

 

图5  net_filter结构体

虚拟网卡对veth pair

    为了与容器中的节点通信,CORE会为每个节点创建一个虚拟网卡对,并将一端移至容器中。

#将虚拟网卡对

/sbin/ip  link  add  name  n1.eth0.44  type  veth  peer  name  n1.0.44

#将虚拟网卡对的一端加入到容器的命名空间中,并命名为eth0

/sbin/ip  link  set  n1.0.44  netns  17122     #17122为节点n1的vnoded进程的ID

   

虚拟网卡是去掉了硬件相关操作的网卡驱动程序。与物理网卡不一样,它没有接收外部数据的功能,只能由别的模块给它发送数据,通过调用veth_xmit实现。

stats->tx_bytes += length;

       stats->tx_packets++;

       rcv_stats->rx_bytes += length;

       rcv_stats->rx_packets++;

       netif_rx(skb);

       在veth_xmit实现中,veth会同时将自己的接收和发送数据统计增加,并调用netif_rx(skb)将skb包交给上层处理。

丢包分析

       CORE在虚拟网络里传递数据包时,都是通过Linux的软中断机制进行。当数据流量特别大的时候,软中断守护进程(ksoftirqd)负载会很大,有时候会达到100%,这时候可能发生丢包。这种丢包与网络协议会对格式、校验进行检查后做的丢弃坏包不一样,是CORE网络仿真系统的瓶颈。我们看一下netif_rx函数。

int netif_rx(struct sk_buff *skb)

{

       struct softnet_data *queue;

       unsigned long flags;

       /* if netpoll wants it, pretend we never saw it */

       if (netpoll_rx(skb))     return NET_RX_DROP; 

       if (!skb->tstamp.tv64)   net_timestamp(skb);     /* 时间戳处理*/

 

       local_irq_save(flags);                         /* 保存当前中断状态,关中断*/

       queue = &__get_cpu_var(softnet_data);           /* 时间戳处理*/

       __get_cpu_var(netdev_rx_stat).total++;

       if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {

              if (queue->input_pkt_queue.qlen) {                   /* 插入调度队列*/

enqueue:

                     __skb_queue_tail(&queue->input_pkt_queue, skb);   /* 插入调度队列*/

                     local_irq_restore(flags);               /* 打开中断*/

                     return NET_RX_SUCCESS;          

              }

              napi_schedule(&queue->backlog);           /*唤醒ksoftirqd进行队列处理*/

              goto enqueue;

       }

       __get_cpu_var(netdev_rx_stat).dropped++;       

       local_irq_restore(flags);                        /* 打开中断*/

       kfree_skb(skb);

       return NET_RX_DROP;

}

netif_rx函数会将skb包加入到当前cpu的softnet_data队列,如果队列为满,则丢弃数据包操作。可以通过cpu的netdev_rx_stat状态查看到丢包的状态。

 

CORE网络数据包接收传递过程分析

标签:

原文地址:http://www.cnblogs.com/gqyan82/p/4656679.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!