修改beacon帧结构(1)帧的写入和发送过程【linux内核-OpenWRT】

    最近需要对OpenWRT内核中的beacon帧做修改,在网上找了很久也没找到相关的帖子,现在自己把修改的方法给弄明白了,写出来供大家参考一下,里面有错误的地方希望诸位大神能给我指出来,小白先在此谢过了。

    要修改beacon帧,就需要了解帧的写入和发送的过程,今天我们就来研究一下beacon帧的写入和发送过程。


beacon帧发送机制:
    beacon帧的发送是通过tasklet机制实现的,tasklet是软中断实现的下半部处理机制,用于中断处理流程的下半部。核心函数是beacon.c中的ath9k_beacon_tasklet函数,(将该函数的指针传递给tasklet_init()即可实现tasklet_struct的动态创建,当tasklet被调度以后,ath9k_beacon_tasklet函数会被执行)。函数体如下所示:

 1 void ath9k_beacon_tasklet(unsigned long data)
 2 {
 3     struct ath_softc *sc = (struct ath_softc *)data;
 4     struct ath_hw *ah = sc->sc_ah;
 5     struct ath_common *common = ath9k_hw_common(ah);
 6     struct ath_buf *bf = NULL;
 7     ...
 8     
 9     bf = ath9k_beacon_generate(sc->hw, vif);
10     
11     ...    
12     if (bf) {
13         ath9k_reset_beacon_status(sc);
14 
15 
16         ath_dbg(common, BEACON, "Transmitting beacon for slot: %d\n", slot);
17 
18 
19         /* NB: cabq traffic should already be queued and primed */
20         ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr);
21         
22         if (!edma)
23         {
24             ath9k_hw_txstart(ah, sc->beacon.beaconq);
25         }
26     }
27 }

 


       struct ath_softc中的struct ath_beacon_config cur_beacon_conf成员中定义了名为beacon_interval的int型变量,每隔一个beacon_interval(即beacon帧的帧间隔),系统就尝试发送一个beacon帧,此时开启中断,tasklet就被调度,执行ath9k_beacon_tasklet函数,该函数就会调用ath9k_beacon_generate生成beacon帧,然后调用ath9k_hw_puttxbuf将beacon帧放入发送缓存队列中,接着调用ath9k_hw_txstart将beacon帧发送出去。
        研究收发机制是为了修改beacon帧,因此接下来我们看一看beacon帧是如何产生的。这就要研究刚才提到的ath9k_beacon_generate函数了。ath9k_beacon_generate函数体如下所示:

 1 static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 2 {
 3     struct ath_softc *sc = hw->priv;
 4     struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 5     struct ath_buf *bf;
 6     struct ath_vif *avp = (void *)vif->drv_priv;
 7     struct sk_buff *skb;
 8     struct ath_txq *cabq = sc->beacon.cabq;
 9     struct ieee80211_tx_info *info;
10     struct ieee80211_mgmt *mgmt_hdr;
11     int cabq_depth;
12 
13 
14     if (avp->av_bcbuf == NULL)
15         return NULL;
16 
17 
18     bf = avp->av_bcbuf;
19     skb = bf->bf_mpdu;
20     if (skb) {
21         dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE);
22         dev_kfree_skb_any(skb);
23         bf->bf_buf_addr = 0;
24         bf->bf_mpdu = NULL;                /*清空缓存*/
25     }
26 
27 
28     
29     skb = ieee80211_beacon_get(hw, vif);
30 
31         
32     if (skb == NULL)
33         return NULL;                    /*skb生成失败退出*/
34     
35     bf->bf_mpdu = skb;                    /*将生成的beacon帧缓存赋给bf结构体,此处是指针赋值,可以只用任一指针对对象进行修改*/
36 
37 
38     
39 
40 
41     mgmt_hdr = (struct ieee80211_mgmt *)skb->data;
42     mgmt_hdr->u.beacon.timestamp = avp->tsf_adjust;        /*用ieee80211_mgmt结构体将skb->data中的前面若干个字段提取出来(包括frame_control,duration,da,sa,bssid,seqctrl,以及beacon帧特有的timestamp,beacon interval,capability information,variable字段),并对其timestamp字段进行数据的写入*/
43 
44 
45     ...
46     
47     return bf;
48 }

 

 

      在ath9k_beacon_generate中,生成beacon帧的主要函数是ieee80211_beacon_get,该函数调用ieee80211_beacon_get_tim进行beacon帧的生成,返回一个struct sk_buff型的结构体。

插播一个概念——对sk_buff结构体做一个介绍:sk_buff是Linux网络代码中最重要的结构体之一。它是Linux在其协议栈里传送的结构体,也就是所谓的“包”,在他里面包含了各层协议的头部,比如ethernet, ip ,tcp ,udp等等。也有相关的操作等。熟悉他是进一步了解Linux网络协议栈的基础。该结构体可以看作一个指针的集合,里面存储了指向数据区域的指针(char *data指针就指向了待发送的beacon的信息)。另外还包含了一些指标性的变量(比如头部长度、数据长度、缓冲区大小等)。下面可以看到beacon数据就是写入到该结构体中去的,在后面的接收部分可以看到数据的读取也是通过sk_buff完成的。

插播结束,下面我们再来看一看ieee80211_beacon_get_tim的函数体,看看是如何生成beacon帧的:

 1 struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 *tim_offset, u16 *tim_length)
 2 {
 3     struct ieee80211_local *local = hw_to_local(hw);
 4     struct sk_buff *skb = NULL;
 5     struct ieee80211_tx_info *info;
 6     struct ieee80211_sub_if_data *sdata = NULL;
 7     enum ieee80211_band band;
 8     struct ieee80211_tx_rate_control txrc;
 9     struct ieee80211_chanctx_conf *chanctx_conf;
10 
11 
12     rcu_read_lock();
13 
14 
15     sdata = vif_to_sdata(vif);                    /*从虚拟接口中读取信息*/
16     chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
17 
18 
19     ...
20 
21 
22     if (sdata->vif.type == NL80211_IFTYPE_AP) {            /*对虚拟接口类型进行判断,对于AP/ADHOC/MESH,要采取不同的beacon生成方式*/
23         struct ieee80211_if_ap *ap = &sdata->u.ap;            
24         struct beacon_data *beacon = rcu_dereference(ap->beacon);    /*根据从虚拟接口中读取的信息,生成beacon_data的基本数据结构*/
25 
26 
27         if (beacon) {
28             /*
29              * headroom, head length,
30              * tail length and maximum TIM length
31              */
32             skb = dev_alloc_skb(local->tx_headroom + beacon->head_len + beacon->tail_len + 256);/*分配内存,分配缓冲区和一个sk_buff结构*/
33             if (!skb)
34                 goto out;
35 
36 
37             skb_reserve(skb, local->tx_headroom);
38         memcpy(skb_put(skb, beacon->head_len), beacon->head, beacon->head_len); /*根据beacon_data结构,将头部的信息(TIM之前的)写入beacon帧,部分字段先预留等待后续程序的写入*/
39 
40 
41             ieee80211_beacon_add_tim(sdata, &ap->ps, skb);            /*将TIM信息写入sk_buffer中*/
42 
43 
44             if (tim_offset)
45                 *tim_offset = beacon->head_len;
46             if (tim_length)
47                 *tim_length = skb->len - beacon->head_len;
48 
49 
50             if (beacon->tail)
51                 memcpy(skb_put(skb, beacon->tail_len),
52                        beacon->tail, beacon->tail_len);
53         } else
54             goto out;
55     } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
56         
57         ...
58     
59     } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
60         
61         ...
62 
63 
64     } else {
65         WARN_ON(1);
66         goto out;
67     }
68 
69 
70     ...    
71 
72 
73  out:
74     rcu_read_unlock();
75     return skb;
76 }
77 EXPORT_SYMBOL(ieee80211_beacon_get_tim);

 


    程序的执行过程在注释中已经写出来了。beacon帧的帧结构分为几个部分,MAC头部分、强制字段部分和可选字段部分,可选字段不一定会用到,只有需要用到时才会出现,可选字段中包括了若干保留字段,如果要对帧结构字段作增加,则需要利用这部分保留字段(每个字段至少要包含ELEMENT ID、字段长度和字段内容三部分的信息)。MAC头部信息和强制字段是通过memcpy函数写入的,在memcpy函数对beacon的写入过程中,所需信息是从虚拟接口获得的,在写入的时候采用直接复制的方法,将MAC头部信息和强制字段(Mandatory)写入sk_buff。为了研究选择性字段(Optional)如何写入,我们接下来需要对TIM的添加方法进行一下梳理,看一看ieee80211_beacon_add_tim函数的执行过程——ieee80211_beacon_add_tim在自旋锁上锁的条件下调用__ieee80211_beacon_add_tim函数,如果自旋锁处在解锁状态则对自旋锁上锁然后调用__ieee80211_beacon_add_tim函数,下面研究一下__ieee80211_beacon_add_tim函数:

 1 static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
 2                        struct ps_data *ps, struct sk_buff *skb)
 3 {
 4     u8 *pos, *tim;                /*pos是一个重要的指针,下面将会看到程序通过指针偏移的方法对beacon帧进行字段的写入*/
 5     int aid0 = 0;
 6     int i, have_bits = 0, n1, n2;
 7 
 8 
 9     ...
10 
11 
12     tim = pos = (u8 *) skb_put(skb, 6);    /*skb_put是sk_buff的管理函数,的用途是获取当前sk_buff的尾部指针位置,同时根据当前要写入的数据的总长度对sk_buff的大小进行调整*/
13     *pos++ = WLAN_EID_TIM;            /*这里就是TIM的写入过程,通过指针偏移,分别以字节为单位写入ELEMENT ID,字段长度值,字段内容值*/
14     *pos++ = 4;
15     *pos++ = ps->dtim_count;
16     *pos++ = sdata->vif.bss_conf.dtim_period;
17 
18 
19     if (ps->dtim_count == 0 && !skb_queue_empty(&ps->bc_buf))
20         aid0 = 1;
21 
22 
23     ps->dtim_bc_mc = aid0 == 1;
24 
25 
26     if (have_bits) {
27         /* Find largest even number N1 so that bits numbered 1 through
28          * (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits
29          * (N2 + 1) x 8 through 2007 are 0. */
30         n1 = 0;
31         for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) {
32             if (ps->tim[i]) {
33                 n1 = i & 0xfe;
34                 break;
35             }
36         }
37         n2 = n1;
38         for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) {
39             if (ps->tim[i]) {
40                 n2 = i;
41                 break;
42             }
43         }
44 
45 
46         /* Bitmap control */
47         *pos++ = n1 | aid0;
48         /* Part Virt Bitmap */
49         skb_put(skb, n2 - n1);
50         memcpy(pos, ps->tim + n1, n2 - n1 + 1);
51 
52 
53         tim[1] = n2 - n1 + 4;
54     } else {
55         *pos++ = aid0; /* Bitmap control */
56         *pos++ = 0; /* Part Virt Bitmap */
57     }
58 
59 
60 }

 

经过这样一个流程以后,beacon帧的信息就被写入并发送出去了。可以看出,在ath9k_beacon_generate函数中,程序会按照帧结构的字段次序在相应的位置写入beacon帧的帧信息。这给我们下一步对帧字段的添加、帧结构的修改提供了参考。【未完待续】

转载于:https://www.cnblogs.com/ussam/p/3623919.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值