OpenWRT数据接收过程【Linux内核-OpenWRT】

OpenWRT数据接收过程 这里使用的是ath9k网卡驱动,硬件平台是TP-link TL-WR841N V7.1 路由器

1.  ieee80211_tasklet_handler()

Linux内核是通过中断来对接收到的数据进行响应的。当硬件检测到有接收数据的时候,产生一个中断,中断触发下半部的tasklet机制,在802.11协议栈这里会调用ieee80211_tasklet_handler()函数。我们来看一看函数体:(位于OpenWRT内核文件夹子目录/net/mac80211,文件main.c中)

static void ieee80211_tasklet_handler(unsigned long data)
{
       struct ieee80211_local *local = (struct ieee80211_local *) data;
       struct sk_buff *skb;
       while ((skb = skb_dequeue(&local->skb_queue)) ||
              (skb = skb_dequeue(&local->skb_queue_unreliable))) {
              switch (skb->pkt_type) {
              case IEEE80211_RX_MSG:
                     /* Clear skb->pkt_type in order to not confuse kernel
                      * netstack. */
                     skb->pkt_type = 0;
                     ieee80211_rx(&local->hw, skb);
                     break;
              case IEEE80211_TX_STATUS_MSG:
                     ...
              default:
                     ...
              }
       }
}

2.  ieee80211_rx()

系统收到数据时会开辟一个sk_buff缓存空间进行数据的存储,ieee80211_tasklet_handler()触发后对sk_buff中存储的数据帧进行判断,如果是接收来的数据(MPDU),则进入ieee80211_rx()函数:(位于OpenWRT内核文件夹子目录/net/mac80211,文件rx.c中)。

void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
       struct ieee80211_local *local = hw_to_local(hw);
       struct ieee80211_rate *rate = NULL;
       struct ieee80211_supported_band *sband;
       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
       ...
       __ieee80211_rx_handle_packet(hw, skb);
       rcu_read_unlock();
       return;
 drop:
       kfree_skb(skb);
}
EXPORT_SYMBOL(ieee80211_rx);

3.  __ieee80211_rx_handle_packet()

ieee80211_rx()函数再调用__ieee80211_rx_handle_packet()(位于OpenWRT内核文件夹子目录/net/mac80211,文件rx.c中),__ieee80211_rx_handle_packet()是接收帧的处理函数,会对帧类型进行判断,如果检测出该帧是beacon帧(或sta主动扫描后从AP端返回的响应帧),则进入ieee80211_scan_rx()函数对帧信息进行扫描,如果是数据帧,则调用ieee80211_prepare_and_rx_handle()对帧进行处理,下面分析接收帧为数据帧的情况。

static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, struct sk_buff *skb)
{
       struct ieee80211_local *local = hw_to_local(hw);
       struct ieee80211_sub_if_data *sdata;
       struct ieee80211_hdr *hdr;
       __le16 fc;
       struct ieee80211_rx_data rx;
       struct ieee80211_sub_if_data *prev;
       struct sta_info *sta, *tmp, *prev_sta;
       int err = 0;
       ...
       hdr = (struct ieee80211_hdr *)skb->data;
       ieee80211_parse_qos(&rx);
       ieee80211_verify_alignment(&rx);
       if (unlikely(ieee80211_is_probe_resp(hdr->frame_control) ||
                   ieee80211_is_beacon(hdr->frame_control)))
       {
              ieee80211_scan_rx(local, skb);              /*扫描帧信息*/
              }
       if (ieee80211_is_data(fc)) {
              prev_sta = NULL;
              for_each_sta_info(local, hdr->addr2, sta, tmp) {
                     if (!prev_sta) {
                            prev_sta = sta;
                            continue;
                     }
                     rx.sta = prev_sta;
                     rx.sdata = prev_sta->sdata;
                     ieee80211_prepare_and_rx_handle(&rx, skb, false);
                     prev_sta = sta;
              }
              ...
       }
       ...
 out:
       dev_kfree_skb(skb);
}

4.  ieee80211_prepare_and_rx_handle()

调用ieee80211_prepare_and_rx_handle()(位于OpenWRT内核文件夹子目录/net/mac80211,文件rx.c中)。

/*
 * This function returns whether or not the SKB
 * was destined for RX processing or not, which,
 * if consume is true, is equivalent to whether
 * or not the skb was consumed.
 */
static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, struct sk_buff *skb, bool consume)
{
…
       ieee80211_invoke_rx_handlers(rx);
       return true;
}

5.  ieee80211_invoke_rx_handlers()

调用ieee80211_invoke_rx_handlers()(位于OpenWRT内核文件夹子目录/net/mac80211,文件rx.c中)。

static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
{
…
       ieee80211_rx_reorder_ampdu(rx, &reorder_release);
       ieee80211_rx_handlers(rx, &reorder_release);
       return;
 rxh_next:
       ieee80211_rx_handlers_result(rx, res);
…
}

6.  ieee80211_rx_handlers()

调用ieee80211_rx_handlers()(位于OpenWRT内核文件夹子目录/net/mac80211,文件rx.c中)。

static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
{
       ieee80211_rx_result res = RX_DROP_MONITOR;
       struct sk_buff *skb;
#define CALL_RXH(rxh)                            do {                                          res = rxh(rx);                            if (res != RX_CONTINUE)                         goto rxh_next;         } while (0);
       spin_lock_bh(&rx->local->rx_path_lock);
       while ((skb = __skb_dequeue(frames))) {
              /*
               * all the other fields are valid across frames
               * that belong to an aMPDU since they are on the
               * same TID from the same station
               */
              rx->skb = skb;
              CALL_RXH(ieee80211_rx_h_decrypt)
              CALL_RXH(ieee80211_rx_h_check_more_data)
              CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll)
              CALL_RXH(ieee80211_rx_h_sta_process)
              CALL_RXH(ieee80211_rx_h_defragment)
              CALL_RXH(ieee80211_rx_h_michael_mic_verify)
              /* must be after MMIC verify so header is counted in MPDU mic */
#ifdef CPTCFG_MAC80211_MESH
              if (ieee80211_vif_is_mesh(&rx->sdata->vif))
                     CALL_RXH(ieee80211_rx_h_mesh_fwding);
#endif
              CALL_RXH(ieee80211_rx_h_amsdu)
              CALL_RXH(ieee80211_rx_h_data)
              /* special treatment -- needs the queue */
              res = ieee80211_rx_h_ctrl(rx, frames);
              if (res != RX_CONTINUE)
                     goto rxh_next;
              CALL_RXH(ieee80211_rx_h_mgmt_check)
              CALL_RXH(ieee80211_rx_h_action)
              CALL_RXH(ieee80211_rx_h_userspace_mgmt)
              CALL_RXH(ieee80211_rx_h_action_return)
              CALL_RXH(ieee80211_rx_h_mgmt)
 rxh_next:
              ieee80211_rx_handlers_result(rx, res);
#undef CALL_RXH
       }
       spin_unlock_bh(&rx->local->rx_path_lock);
}

7.  ieee80211_rx_h_data()

只看是数据帧的情况,会继续调用ieee80211_rx_h_data()(位于OpenWRT内核文件夹子目录/net/mac80211,文件rx.c中)。

static ieee80211_rx_result debug_noinline ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
{
…
       rx->skb->dev = dev;
       dev->stats.rx_packets++;
       dev->stats.rx_bytes += rx->skb->len;
       if (local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 &&
           !is_multicast_ether_addr(
                  ((struct ethhdr *)rx->skb->data)->h_dest) &&
           (!local->scanning &&
            !test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))) {
                     mod_timer(&local->dynamic_ps_timer, jiffies +
                      msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
       }
       ieee80211_deliver_skb(rx);
       return RX_QUEUED;
}

8.  ieee80211_deliver_skb()

调用ieee80211_deliver_skb()(位于OpenWRT内核文件夹子目录/net/mac80211,文件rx.c中)。

/*
 * requires that rx->skb is a frame with ethernet header
 */
static void
ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
{
       …
       skb = rx->skb;
       …
       if (skb) {
              int align __maybe_unused;
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
              /*
               * ‘align‘ will only take the values 0 or 2 here
               * since all frames are required to be aligned
               * to 2-byte boundaries when being passed to
               * mac80211; the code here works just as well if
               * that isn‘t true, but mac80211 assumes it can
               * access fields as 2-byte aligned (e.g. for
               * compare_ether_addr)
               */
              align = ((unsigned long)(skb->data + sizeof(struct ethhdr))) & 3;
              if (align) {
                     if (WARN_ON(skb_headroom(skb) < 3)) {
                            dev_kfree_skb(skb);
                            skb = NULL;
                     } else {
                            u8 *data = skb->data;
                            size_t len = skb_headlen(skb);
                            skb->data -= align;
                            memmove(skb->data, data, len);
                            skb_set_tail_pointer(skb, len);
                     }
              }
#endif
              if (skb) {
                     /* deliver to local stack */
                     skb->protocol = eth_type_trans(skb, dev);
                     memset(skb->cb, 0, sizeof(skb->cb));
                     netif_receive_skb(skb);
              }
       }
       …
}

这里最核心的代码就是netif_receive_skb(skb)了,至此,数据已经接收到并发送至内核的网络子系统去处理。netif_receive_skb定义于内核文件夹linux-3.3.8的子目录/net/core的文件dev.c中。

时间: 2024-12-15 01:48:22

OpenWRT数据接收过程【Linux内核-OpenWRT】的相关文章

OpenWRT数据发送过程【Linux内核-OpenWRT】

之前一篇写的不完整,重新写一篇 OpenWRT数据发送过程 这里使用的是ath9k网卡驱动,硬件平台是TP-link TL-WR841N V7.1 路由器 1.  packet_sendmsg() Linux kernel发送数据的接口函数是packet_sendmsg,本质上对应了user space的sendmsg实现.上层通过调用sendmsg实现数据的发送.将待发送的数据放入kernel space中. 在内核文件夹linux-3.3.8的子目录:/net/packet中,找到文件af_

Spark Streaming 数据接收过程

SparkStreaming 源码分析 一节中从源码角度,描述了Streaming执行时代码的调用过程.下边就接收转化阶段过程再简单分析一下,为分析backpressure作准备. SparkStreaming的全过程分为两个阶段:数据接收转化阶段和Job产生与执行阶段.两个阶段通过数据接收转化阶段产生的Block联系在一起.下图是依据对基于Recevier的数据接收源转化部分源码分析所做. 数据接收转化过程可以分为如下几个关键步骤: Receiver接收外部数据流,其将接收的数据流交由Bloc

分析进程创建的过程---linux内核学习笔记(六)

内容一:实验报告相关说明. 真实姓名 谢润帮 原创作品转载请注明出处  所学课程:<Linux内核分析>MOOC课程   链接:http://mooc.study.163.com/course/USTC-1000029000

精通Linux内核网络

这篇是计算机中操作系统Linux类的优质预售推荐<精通Linux内核网络>. 最详尽的Linux内核网络专著,深入剖析IPsec.Wireless.InfiniBand等重要内核网络子系统. 编辑推荐 专注于各网络协议实现技术的精髓及其遵循的指导方针和原则. 重点讲解数据包在Linux内核网络栈中的传输过程,阐述其与网络各层及各子系统之间的交互. 从网络开发者视角,配合清晰图表,深入剖析Linux内核网络子系统的内部细节及核心实现 内容简介 本书讨论Linux 内核网络栈的实现及其原理,深入而

Linux系统的理解及学习Linux内核的心得

作业列表      linux内核分析作业:以一简单C程序为例,分析汇编代码理解计算机如何工作 linux内核分析作业:操作系统是如何工作的进行:完成一个简单的时间片轮转多道程序内核代码 linux内核分析作业3:跟踪分析Linux内核的启动过程 linux内核分析作业4:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 linux内核分析作业5:分析system_call中断处理过程 linux内核分析作业6:分析Linux内核创建一个新进程的过程 Linux内核分析作业7:L

Linux内核分析学习心得

李洋  原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 转眼几周过去,<Linux内核分析>这门课也眼看就要结束,再这几周的学习中,我在原来的基础上加深了对Linux系统内核的理解,比如很多内存管理,进程管理的知识,原来对于我只是书本上的东西,通过课程和实验却能够通过切实的代码和运行过程的跟踪而表现出来. 这几周的学习心得: 这次学习涉及了Linux内核调度机制,schedu

Linux 内核数据结构:双向链表

Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为广泛的数据结构,具体你可以 查看 这里. 首先让我们看一下主要的结构体: struct list_head { struct list_head *next, *prev; }; 你可以看到其与常见的结构体实现有显著不同,比如 glib 中所使用到的双向链表实现. struct GList { gp

Linux 内核数据结构:Linux 双向链表

Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为广泛的数据结构,具体你可以 查看 这里. 首先让我们看一下主要的结构体: struct list_head { struct list_head *next, *prev; }; 你可以看到其与常见的结构体实现有显著不同,比如 glib 中所使用到的双向链表实现. struct GList { gp

Linux内核分析总结

周子轩 原创作品转载注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 笔记: 冯诺依曼体系结构的核心思想是存储程序计算机.在计算机中有两种指令,一是用户指令,一是系统调用. Linux是一个基于POSIX和UNIX的多用户.多任务.支持多线程和多CPU的操作系统. 当内核运行的时候,系统以内核态进入内核空间执行.而执行一个普通用户程序时,系统将以用户态进入以用户空间执行. 在系统中运行的应用程序通过系统