浅析linux内核网络协议栈--linux bridge(二)

6. 网桥数据转发

6.1 网桥数据包入口

网桥是一种2层网络互连设备,而不是一种网络协议。它在协议结构上并没有占有一席之地,因此不能通过向协议栈注册协议的方式来申请网桥数据包的处理。相 反,网桥接口(如上述的eth1)的数据包和一般接口(如eth0)在格式上完全是一样的,不同之处是网桥在2层上就对它进行了转了,而一般接口要在3层 才能根据路由信息来决定是否要转发,如何转发。那么一个网络接口,在驱动处理完数据包后,怎么才知道该接口分配在一个网桥里面呢?其实很简单,当 brctl工具通过ioctl系统调用时,kernel为该添加的设备生成一个bridge_port结构并放到port_list链中,同时将该 bridge_port的值赋予设备net_device的br_port指针。因此,要识别接口是否属于某个网桥,只需判断net_device的 br_port指针是否不为空即可。

现假设PC1向PC2发送其个数据包,数据首先会由eth1网卡接收,此后网卡向CPU发送接收中断。当CPU执行当前指令后(如果开中断的话),马上跳 到网卡的驱动程去。Eth1的网卡驱动首先生成一个skb结构,然后对以太网层进行分析,最后驱动将该skb结构放到当前CPU的输入队列中,唤醒软中断。如果没有其它中断的到来,那么软中断将调用netif_receive_skb函数。代码和分析如下所述:

int netif_receive_skb(struct sk_buff *skb) {
   //当网络设备收到网络数据包时,最终会在软件中断环境里调用此函数
   //检查该数据包是否有packet socket来接收该包,如果有则往该socket
   //拷贝一份,由deliver_skb来完成。
   list_for_each_entry_rcu(ptype, &ptype_all, list) {
     if (!ptype->dev || ptype->dev == skb->dev) {
       if (pt_prev)
         ret = deliver_skb(skb, pt_prev, orig_dev);
       pt_prev = ptype;
     } 
   }
   // 先试着将该数据包让网桥函数来处理,如果该数据包的入口接口确实是网桥接口,
   // 则按网桥方式来处理,并且handle_bridge返回NULL,表示网桥已处理了。
   // 如果不是网桥接口的数据包,则不应该让网桥来处理,handle_bridge返回skb,
   // 后面代码会让协议栈来处理上层协议。
   skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);
   if (!skb)
     goto out;
   skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev);
   if (!skb)
     goto out;
   //对该数据包转达到它L3协议的处理函数
   type = skb->protocol;
   list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type)&15], list) {
     if (ptype->type == type &&
         (!ptype->dev || ptype->dev == skb->dev)) {
       if (pt_prev)
         ret = deliver_skb(skb, pt_prev, orig_dev);
       pt_prev = ptype;
     }
   }
}
【文章福利】小编推荐自己的Linux内核技术交流群: 【977878001】整理一些个人觉得比较好得学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前100进群领取,额外赠送一份 价值699的内核资料包(含视频教程、电子书、实战项目及代码)

内核资料直通车:Linux内核源码技术学习路线+视频教程代码资料

学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

6.2 网桥处理逻辑

static inline struct sk_buff *handle_bridge(struct sk_buff *skb,
                                            struct packet_type **pt_prev, int *ret,
                                            struct net_device *orig_dev) {
  struct net_bridge_port *port;
  //如果该数据包产生于本机,而目标同时为本机。
  if (skb->pkt_type == PACKET_LOOPBACK ||
     //如果该数据包的输入接口不是网桥接口
     (port = rcu_dereference(skb->dev->br_port)) == NULL)
     // 以上两种情况都需要让上层协议进行处理
    return skb;
  if (*pt_prev) {
    *ret = deliver_skb(skb, *pt_prev, orig_dev);
    *pt_prev = NULL;
  }
  //数据包的入口接口是网桥接口。下面将按网桥逻辑进行处理。
  //如假包换,数据包转达到真正的网桥处理函数
  //br_handle_frame_hook在网桥模块的init函数被初始化为
  //br_handle_frame
  return br_handle_frame_hook(port, skb);
}

这里回调了br_handle_frame_hook()函数,这个是一个钩子函数。Br_handle_frame_hook()函数在Linux2.6.34\net\bridge\Br_input.c中,br_handle_frame_hook=br_handle_frame,所以实际函数为br_handle_frame.

6.3 br_handle_frame

好,现在我们看看bridge端口的处理函数 br_handle_frame如何处理skb和指示后续操作。该函数位于br_input.c中。

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

网桥端口不打算处理回环数据;源地址必须为合法Ethernet地址:源MAC地址不能是全0,不能是MAC广播和多播,是的话就丢弃。

skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
    return RX_HANDLER_CONSUMED;

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值