追蹤ns-2.28802.11的程式碼

 

參考檔案: ns-allinone-2.28/ns-2.28/mac目錄下的mac-timers.[cc,h]mac-802_11.[cc,h]

參考資料: http://www.ece.rice.edu/~jpr/ns/docs/802_11.html

 

如何傳送一個封包(How to transmit a packet?)

        首先,我們要看的第一個function是在mac-802_11.cc內的recv( ),程式會先判斷目前呼叫recv( )這個packet的傳輸方向,若是DOWN,則表示此packet是要送出去的,因此就會再呼叫send(p, h).所以接著,我們跳到send( ),send( )首先會去檢查energy model,若是目前這個node是在睡眠狀態(sleep mode),則把此packet給丟棄.然後會把handler h設定給callback_.下一步,就是去呼叫sendDATA(p)sendRTS(ETHER_ADDR(dh->dh_ra)).

 

        底下是sendDATA的程式碼. (部份英文說明和程式碼,會因為長度的關係而拿掉,所以讀者最好還是拿原本的程式碼做對照)

void Mac802_11::sendDATA(Packet *p)

{

        hdr_cmn* ch = HDR_CMN(p);

        struct hdr_mac802_11* dh = HDR_MAC802_11(p);

 

        /* 更新packet的長度,packet的長度加上PreambleLength (內定值為144 bits), PLCPHeaderLength(內定值為48bits), Mac Header LengthETHER_FCS_LEN */

ch->size() += phymib_.getHdrLen11();

 

        /* 填入Mac Headerframe control內的子欄位值 */

        dh->dh_fc.fc_protocol_version = MAC_ProtocolVersion;

        dh->dh_fc.fc_type       = MAC_Type_Data;

        dh->dh_fc.fc_subtype    = MAC_Subtype_Data;

        //printf(".....p = %x, mac-subtype-%d\n",p,dh->dh_fc.fc_subtype);

       

        dh->dh_fc.fc_to_ds      = 0;

        dh->dh_fc.fc_from_ds    = 0;

        dh->dh_fc.fc_more_frag  = 0;

        dh->dh_fc.fc_retry      = 0;

        dh->dh_fc.fc_pwr_mgt    = 0;

        dh->dh_fc.fc_more_data  = 0;

        dh->dh_fc.fc_wep        = 0;

        dh->dh_fc.fc_order      = 0;

 

    /* 記錄傳送所需要花的時間,計算的方式(PreambleLength +PLCPHeaderLength) * 8 / PLCPDataRate + 剩於的封包長度(單位為bytes) * 8 / dataRate_ */

    /* 事實上,底下的這一行程式碼是個浪費,因為底下又會針對是否為broadcastunicast的封包,再計算一次 */

       ch->txtime() = txtime(ch->size(), dataRate_);

 

        /* 若是這是一個unicast的封包 */

if((u_int32_t)ETHER_ADDR(dh->dh_ra) != MAC_BROADCAST) {

                /* 再一次計算傳送所需要花的時間 */

                ch->txtime() = txtime(ch->size(), dataRate_);

                /* duration的意思是送出去此data packet之後,此次的通訊還需要占用channel所需要的時間,這個時間的長度為傳送一個ACK和一個SIF的時間 */

                dh->dh_duration = usec(txtime(phymib_.getACKlen(), basicRate_)+ phymib_.getSIFS());

        } else {

                /* 若這是一個multicast的封包 */

                ch->txtime() = txtime(ch->size(), basicRate_);

                /* 若是multicast packet,送出去此data packet之後,就算傳送完成,不需要再等待ACK,因此duration0 */

                dh->dh_duration = 0;

        }

        /*Mac Header中的資訊都填完後,我們先把此packet暫時地存放在Mac Layer中的local buffer,等待適當的時機再傳送出去 */

        pktTx_ = p;

}

 

        底下是sendRTS的程式碼. (部份英文說明和程式碼,會因為長度的關係而拿掉,所以讀者最好還是拿原本的程式碼做對照)

void Mac802_11::sendRTS(int dst)

{

        Packet *p = Packet::alloc();

        hdr_cmn* ch = HDR_CMN(p);

        struct rts_frame *rf = (struct rts_frame*)p->access(hdr_mac::offset_);

 

    /* 檢查要傳送的封包大小是否是小於RTSThreshold或是不是一個broadcast的封包,若是的話,就不需要傳送RTS.若是在使用者所寫的tcl中沒有指定RTSThreshold,ns2會去讀取ns-default.tcl的值,內定為0,因此若是使用unicast,則一定會送出去RTS */

        if( (u_int32_t) HDR_CMN(pktTx_)->size() < macmib_.getRTSThreshold() || (u_int32_t) dst == MAC_BROADCAST) {

                Packet::free(p);

                return;

        }

  

        ch->uid() = 0;

        ch->ptype() = PT_MAC;

        ch->size() = phymib_.getRTSlen();

        ch->iface() = -2;

        ch->error() = 0;

 

        bzero(rf, MAC_HDR_LEN);

   

    /* 設定RTS packetMac Header的欄位 */

        rf->rf_fc.fc_protocol_version = MAC_ProtocolVersion;

       rf->rf_fc.fc_type      = MAC_Type_Control;

       rf->rf_fc.fc_subtype = MAC_Subtype_RTS;

       rf->rf_fc.fc_to_ds    = 0;

       rf->rf_fc.fc_from_ds        = 0;

       rf->rf_fc.fc_more_frag     = 0;

       rf->rf_fc.fc_retry      = 0;

       rf->rf_fc.fc_pwr_mgt       = 0;

       rf->rf_fc.fc_more_data    = 0;

       rf->rf_fc.fc_wep      = 0;

       rf->rf_fc.fc_order     = 0;

 

    /* 把要傳送的目的位址存放到RA */

        STORE4BYTE(&dst, (rf->rf_ra));

       

        /* 存放傳送RTS所需要花的時間, RTS Frame是用basicRate_傳送 */

       ch->txtime() = txtime(ch->size(), basicRate_);

       

    /* 把傳送端的位址放到TA */

        STORE4BYTE(&index_, (rf->rf_ta));

 

    /* 計算duration,計算的公式為: SIF + T(CTS) + SIF + T(Pkt) + SIF + T(ACK) */

           rf->rf_duration = usec(phymib_.getSIFS()

                              + txtime(phymib_.getCTSlen(), basicRate_)

                              + phymib_.getSIFS()

                              + txtime(pktTx_)

                              + phymib_.getSIFS()

                              + txtime(phymib_.getACKlen(), basicRate_));

    /* 把建立好的RTS packet先暫時存放到pktRTS_ */

        pktRTS_ = p;

}

 

        看完sendDATA( )sendRTS( )之後,我們再回到send( ).接著,就指定一個unique sequence number給這個data packet.為了更清處的說明,底下把剩餘的程式碼貼在底下.

/ * 這是在send( )內的程式碼 */

/* 若是目前backoff timer並沒有在 count down */

if(mhBackoff_.busy() == 0) {

    /* 此時channel又是idle */

                if(is_idle()) {

                        /* 若是節點已經再等待defer timer,則讓defer timer繼續,因此不做任何的設定.但是若沒有defer timer,就要根據802.11的規定,需要再等待一個DIFS和一個random time才能做資料的傳送,而這個random time是由[0, cw_]所決定的 */

if (mhDefer_.busy() == 0) {

                                rTime = (Random::random() % cw_)*(phymib_.getSlotTime());

                                mhDefer_.start(phymib_.getDIFS() + rTime);

                        }     

            /* 此時channel若是busy */

                else {

                    mhBackoff_.start(cw_, is_idle());

                }

        }

做完以上的事情後, send()已經完成了.

 

        然後,Defer timer expires的時候,程式就會去呼叫deferHandler(),deferHandler()中會先去呼叫check_pktCTRL(),但因為目前pktCTRL沒有資料(回傳-1),所以會繼續去執行check_pktRTS().若是目前channelidle的狀態,check_pktRTS()內的程式碼就會去設定傳輸狀態為MAC_RTC,並且計算送出RTS timeout的時間,算法為:

timeout = txtime(phymib_.getRTSlen(), basicRate_)

                        + DSSS_MaxPropagationDelay                      // 設定為2 us,可以參考mac-802_11.h

                        + phymib_.getSIFS()

                        + txtime(phymib_.getCTSlen(), basicRate_)

                        + DSSS_MaxPropagationDelay;                                        // 設定為2 us,可以參考mac-802_11.h

設定完後,就會去執行transmit(pktRTS_, timeout),RTSpacket送出去.

        送完RTS,我們必需等待CTS,所以我們再回到recv()中的mhRecv_.start(txtime(p)),這個程式碼主要是等待整個packet完全接收後就會去呼叫recvHandler(),recvHandler()就會再去呼叫recv_timer(),若是判斷所收到的packetCTS,則再呼叫recvCTS(pktRx_).recvCTS(),因為已收到CTS,則代表RTS已傳送成功,因此把pktRTS_ = 0ssrc_=0,然後再呼叫tx_resume().

        tx_resume(),由於已成功的做完RTS/CTS,現在要準備送出data.這部份的程式如下所示

/ * 若是pktTx_有資料要傳送 */

else if(pktTx_) {

                if (mhBackoff_.busy() == 0) {

                        hdr_cmn *ch = HDR_CMN(pktTx_);

                        struct hdr_mac802_11 *mh = HDR_MAC802_11(pktTx_);

                        /* 判斷packet size是否小於RTSThreshold或者是不是broadcast */

                        if ((u_int32_t) ch->size() < macmib_.getRTSThreshold() || (u_int32_t) ETHER_ADDR(mh->dh_ra) == MAC_BROADCAST) {

                                rTime = (Random::random() % cw_) * phymib_.getSlotTime();

                                mhDefer_.start(phymib_.getDIFS() + rTime);

            } else {

                        /* 若是unicastpacket size大於RTSThreshold,則會等待一個SIFS,再把data packet送出去 */

                                mhDefer_.start(phymib_.getSIFS());

            }

                }

}

        等到defer timer expires,又會呼叫deferHandler(),而在deferHandler()又會再去呼叫check_pktTx(). check_pktTx()的程式碼如下:

int Mac802_11::check_pktTx()

{

        struct hdr_mac802_11 *mh;

        double timeout;

 

        assert(mhBackoff_.busy() == 0);

 

        if(pktTx_ == 0)

                return -1;

 

        mh = HDR_MAC802_11(pktTx_);

 

        switch(mh->dh_fc.fc_subtype) {

        case MAC_Subtype_Data:

        /* 若是目前的channelbusy的話,就需要增加contention window,然後再執行一次backoff */

                if(! is_idle()) {

                        sendRTS(ETHER_ADDR(mh->dh_ra));

                        inc_cw();

                        mhBackoff_.start(cw_, is_idle());

                        return 0;

                }

            /* 設定傳輸狀態為MAC_SEND */

                setTxState(MAC_SEND);

                if((u_int32_t)ETHER_ADDR(mh->dh_ra) != MAC_BROADCAST)

                        timeout = txtime(pktTx_)

                               + DSSS_MaxPropagationDelay              // 設定為2 us,可以參考mac-802_11.h

                               + phymib_.getSIFS()

                               + txtime(phymib_.getACKlen(), basicRate_)

                               + DSSS_MaxPropagationDelay;             // 設定為2 us,可以參考mac-802_11.h

                else

                        timeout = txtime(pktTx_);

                break;

        default:

                fprintf(stderr, "check_pktTx:Invalid MAC Control subtype\n");

                exit(1);

        }

    /* pktTx_內的data packet傳送出去 */

        transmit(pktTx_, timeout);

        return 0;

}

        資料送出去後,若是unicastdata packet就需要等待ACK,所以我們再回到recv()中的mhRecv_.start(txtime(p)),這個程式碼主要是等待整個packet完全接收後就會去呼叫recvHandler(),recvHandler()就會再去呼叫recv_timer(),若是判斷所收到的packetACK,則會呼叫recvACK(pktRx_). 由於已成功收到ACK,則表示DATA packet已成功的送出,所以就把mhSend_.stop(),判斷packet size是否有大於RTSThreshold,若是有大於的話,就把slrc_ = 0,沒有的話,就把ssrc_=0.並且把pktTx_=0. 最後在結束之前,再呼叫tx_resume().tx_resume()會呼叫callback_....然後整個DATA packet傳送過程就結束了.

 

工作進度

1.      2005/11/07 完成如何傳送一個封包.

2.      2005/11/09 完成how to send a multicast packet over 802.11 network.ppt

3.      2005/11/09 完成how to receive a multicast packet.ppt

4.      2005/11/09 完成How to send a unicast packet.ppt

5.      2005/11/09 完成How to receive a unicast packet.ppt

 

Contact Information

柯志亨 smallko@ee.ncku.edu.tw