Friday, November 12, 2010

Analysis of 802.11 MAC code in NS-2

0. MAC in ns-2


LAN is within Berkeley Architecture, WLAN cannot create with “newLan” command
Ethernet could be created as a LAN with common bandwidth and delay.

1. The general structure of MAC

related sourcecode:  mac.cc,  mac.h

MAC class is derived from Bi-Connector class.

Local Variables:
  • pktTx_ 
  • pktRx_
  • Macstate_ :
  • index_  : mac address

Basic functions of General MAC class.
  • recv (packet, handler)
    • This is the entry from upper target (a callback handler is given as a parameter) to send a packet to MAC. After the MAC transmit this packet successfully, it will use this callback handler to inform the upper target that MAC is idle and  give another packet if there are packets buffered.
  • SendUp
    • entry for receiving a packet. Sendup is function is directly called by the lower target of MAC, might be "netif" or "phy". And this function directly calls the upper_target. Because the uplink  to upper_target does not involve any phsical transmission delay, it does not need any timer and state change here. The question is that when the MAC is in MAC_RECV state? The answer is: The MAC here is supposed to be full-duplex and receive can be happened simultaneously. it does not care about collisions etc. This is a general MAC class
  • SendDown
    • used to sending packet down. Called by recv().init a timer for tx, and the timer handler is defined to call resume().
  • Handler* callback_;
    • when MAC is idle, the upper target has to be callback.
  • Resume()
    • When tx timer out, reset MAC as idle state and callback.
  • Discard
    • When a packet has to be drop, the drop_ (NsObject*) of bi-connector class has to be called to handle this, usually drop (p, why) is used. Why is a string of drop reason, in cmu-trace.h. three-character string is defined to describe those reasons in the trace file, such as "BSY", "CBK"....
mac_state

2. The 802.11 MAC

it is implemented by CMU. It is a subclass of MAC.

related sourcecode : mac-802_11.cc, mac-802_11.h, mac-timer.cc, mac-timer.h

State Machine: 


The tx_state and rx_state are two member variables. Although, actually mac cannot transmit and receive at the same time. we still hold two variables for them. The possible state is MAC_SEND, MAC_IDLE, MAC_RECV, MAC_COLL, MAC_RTS, MAC_CTS, MAC_ACK.

Local Variables:
  • pktTx_ (inherited from Mac class): 
  • pktRx_(inherited from Mac class):
  • pktCtrl_
  • pktRTS_

Basic functions:

  • recv(): the recv() function of MAC class has been overridden. Here, recv() means an incoming packet from both physical layer and upper layer.
  • send(): this function is called by recv() when it get a packet supposed to sending down.  Similarly, a callback handler is given as a parameter. Immediately, the sendDATA(p) function is called to generate an appropriate mac_header for this packet, and set this to pktTx_. Then,  follew the CSMA/CA procedures to get this packet sent.
  • sendDATA(). to compose a DATA MAC frame and set it as pktTx_
  • recvDATA(). When heard a DATA MAC frame, hand it to upper layer.

Callback to IFQ in MAC 802.11

The callback is set when a packet comes from upper layer and be handled by send(p,h). The callback is used and reset to zero in tx_resume() function of mac-802_11.cc:

void
Mac802_11::tx_resume()
{
 .....
else if(callback_) {
        Handler *h = callback_;
        callback_ = 0;
        h->handle((Event*) 0);
    }
    // change wrt Mike's code
    //SET_TX_STATE(MAC_IDLE);
   setTxState(MAC_IDLE);
}
The callback will reactivate the interface queue between mac_ and ll_ objects, see queue/queue.cc
void QueueHandler::handle(Event*)
{
        queue_.resume();
}

void Queue::resume()
{
        Packet* p = deque();
        if (p != 0) {
                target_->recv(p, &qh_);
        } else {
                if (unblock_on_resume_)
                        blocked_ = 0;
                else
                        blocked_ = 1;
        }
}
See that the queue is blocked if not "callback", thus, callback is essential to make queue work properly.Backoff
The backoff implementation in mac-802_11.cc does not comply with the standard. Basically, this is due to the introduction of a defer timer. The defertimer is set in send() and after a packet transmission finishes. The deferred time value is usually (DIFS+rTime) where rTime is as same as the selection of backoff timeslots from CW (Contention Window). However, the defertimer is never paused or resumed.
So, after a packet transmission, according to the IEEE 802.11 standard, the node should do backoff immediately. here in ns-code, the defertime is first set. And after timer out, in check_pktTx() function. if the channel is not idle, a backoff timer is set. Therefore, here exists a double backoff problem. Also, according to IEEE 802.11 standard, a node should access channel directly if it sense the channel idle and still idle after at least DIFS time. Here, ns-code also deviates from the standard and add an additional rTime before sending DATA or RTS.
 
Determine the transmission time 


txtime is calculated from the "size" variable in common header. Because all headers are present in the packet no matter what layer it is. Thus, the only way to know the packet size is to add or subtract some bytes in hdr_cmn->size(); In 802.11 MAC code, in recvDATA(), the 802.11 header size will be subtracted. And in SendDATA(), a 80211_hdr_length will be added.

Transmission Failure:
In both RetransmitRTS and RetransmitDATA functions, it is necessary to deal a packet drop :
if(ssrc_ >= macmib_.getShortRetryLimit()) {
  discard(pktRTS_, DROP_MAC_RETRY_COUNT_EXCEEDED); pktRTS_ = 0;
  /* tell the callback the send operation failed 
     before discarding the packet */
  hdr_cmn *ch = HDR_CMN(pktTx_);
  if (ch->xmit_failure_) {
                        /*
                         *  Need to remove the MAC header so that 
                         *  re-cycled packets don't keep getting
                         *  bigger.
                         */
   // change wrt Mike's code
                        //ch->size() -= ETHER_HDR_LEN11;
   ch->size() -= phymib_.getHdrLen11();
                        ch->xmit_reason_ = XMIT_REASON_RTS;
                        ch->xmit_failure_(pktTx_->copy(),
                                          ch->xmit_failure_data_);  //callback by upperlayer
                }
  //printf("(%d)....discarding RTS:%x\n",index_,pktRTS_);
  discard(pktTx_, DROP_MAC_RETRY_COUNT_EXCEEDED); pktTx_ = 0;
  ssrc_ = 0;
  rst_cw();
 } 
So, it is a way to let upper layer , such as DSR routing to know there is a route-error and need to send a route-error message.

Frame Formats in ns-2

The 802.11 header in ns-2 is not conform to the 802.11 standard. It is a different one. Thus, this overhead cannot be regarded as authentic as that of
the experiment. And the throughput measurements also seems different.

Other IEEE 802.11 features

1. Carrier Sense

This is normally done by the physical layer source code. The state of 802.11 MAC is  controlled by  some timers. Such as the  Backoff_timer. By checking if those timers are still busy, the MAC will arrange its operations correspondingly.

2. Virtual Carrier SenseRTS/CTS is an important part of code. There are functions like sendRTS ( is to form a RTS ctrl packrt) , retransmitRTS...... Basically it has to compare with RTSThreshold first, if the size is small than the threshold, there is no" virtual carrier sense" scheme used.

No comments: