Try   HackMD

802.11 - RTS/CTS

課程影片

第 3C 講 IEEE 802.11 無線區域網路 (Wireless LAN) L03 3

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

RTS/CTS

在送真的封包之前,先廣播一個 request to send 封包。這個封包不只有目的地會收到,發送者傳輸範圍內的其他裝置也會收到。而目的地如果順利收到這個 RTS 的封包,則必須廣播一個 clear to send (CTS) 封包,代表已經準備好接收封包。舉例來說,假定

B 要送一個封包給
C

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

這時,

B 會廣播一個 RTS 封包:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

這個封包送出去之後,如果

C 有收到這個 RTS 封包,就要也廣播一個 CTS 封包:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

並且規定:聽到 CTS 廣播的那些節點,也就是上圖中的紅色節點,在一段足夠向

C 傳輸完訊息的時間內,都不能向
C
傳輸訊息(preempt_disable()local_irq_disable() 的既視感)。這個「某段時間」會記錄在 CTS 封包的 duration_id 欄位,所以所有收到 CTS 的那些節點(也就是「離
C
太近的那些節點」)都會知道在多久的時間之內不能打擾
C
。這樣就保證「能到達
C
的那些節點」在
B
C
傳完封包之前,不會同時往
C
傳輸。

需要注意的是:雖然禁止這些紅色節點往

C 傳送訊息,但是這些節點仍然可對
C
以外的節點通訊。

而這也是 802.11 對 hidden node problem 的解法。因為「能到達

C」是 hidden point 的必要條件,所以排除所有這樣的點就排除了所有可能的 hidden point

節點會維護一個存放不同節點 duration 的資料結構,稱為 Network Allocation Vector。在一個節點閒置時,在其他節點的 NAV 中,該節點所對應的數值預設為 0。而 RTS/CTS 發生時,陣列中表示該節點的元素就會被設為 duration_id 所對應的數值。

有 RTS,無 CTS = RTS 發生碰撞

這個作法有另外一個問題:如果兩個 RTS 封包發生碰撞怎麼辦?解法是:因為協定中,傳了一個 RTS,就必須收到一個 CTS 作為回應。因此,若發現傳送了 RTS,但沒有收到 CTS,這就表示 RTS 並沒有成功傳輸,因此需要重送 RTS。而這個「重送」之前,會等待一段隨機時間(即:exponential back-off),之後再重送 RTS

RTS Threshold

如果準備傳送的封包太小,那這時候如果傳送 RTS 就顯得多此一舉。因此只有大到一定程度的封包需要使用 RTS/CTS 交握,否則就直接送封包即可。而兩者的長度界線稱為 RTS threshold。而 include/linux/ieee80211.h 中也有定義:

/* miscellaneous IEEE 802.11 constants */
#define IEEE80211_MAX_FRAG_THRESHOLD	2352
#define IEEE80211_MAX_RTS_THRESHOLD	2353
#define IEEE80211_MAX_AID		2007
#define IEEE80211_MAX_AID_S1G		8191
#define IEEE80211_MAX_TIM_LEN		251
#define IEEE80211_MAX_MESH_PEERINGS	63

核心中的相關結構

老樣子,都是用對齊好的結構體來表示。

RTS 的結構 - ieee80211_rts

定義在 include/linux/ieee80211.h 中:

struct ieee80211_rts {
	__le16 frame_control;
	__le16 duration;
	u8 ra[ETH_ALEN];
	u8 ta[ETH_ALEN];
} __packed __aligned(2);

CTS 的結構 - ieee80211_cts

一樣定義在 include/linux/ieee80211.h 中:

struct ieee80211_cts {
	__le16 frame_control;
	__le16 duration;
	u8 ra[ETH_ALEN];
} __packed __aligned(2);

核心中的相關函式

生成 RTS - ieee80211_rts_get

宣告在 include/net/mac80211.h 中:

/**
 * ieee80211_rts_get - RTS frame generation function
 * @hw: pointer obtained from ieee80211_alloc_hw().
 * @vif: &struct ieee80211_vif pointer from the add_interface callback.
 * @frame: pointer to the frame that is going to be protected by the RTS.
 * @frame_len: the frame length (in octets).
 * @frame_txctl: &struct ieee80211_tx_info of the frame.
 * @rts: The buffer where to store the RTS frame.
 *
 * If the RTS frames are generated by the host system (i.e., not in
 * hardware/firmware), the low-level driver uses this function to receive
 * the next RTS frame from the 802.11 code. The low-level is responsible
 * for calling this function before and RTS frame is needed.
 */
void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
		       const void *frame, size_t frame_len,
		       const struct ieee80211_tx_info *frame_txctl,
		       struct ieee80211_rts *rts);

定義則是在 net/mac80211/tx.c

void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
		       const void *frame, size_t frame_len,
		       const struct ieee80211_tx_info *frame_txctl,
		       struct ieee80211_rts *rts)
{
	const struct ieee80211_hdr *hdr = frame;

	rts->frame_control =
	    cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS);
	rts->duration = ieee80211_rts_duration(hw, vif, frame_len,
					       frame_txctl);
	memcpy(rts->ra, hdr->addr1, sizeof(rts->ra));
	memcpy(rts->ta, hdr->addr2, sizeof(rts->ta));
}
EXPORT_SYMBOL(ieee80211_rts_get);

計算 RTS 的 duration_id 欄位 - ieee80211_rts_duration

定義在 net/mac80211/util.c 中,宣告在 include/net/mac80211.h

/**
 * ieee80211_rts_duration - Get the duration field for an RTS frame
 * @hw: pointer obtained from ieee80211_alloc_hw().
 * @vif: &struct ieee80211_vif pointer from the add_interface callback.
 * @frame_len: the length of the frame that is going to be protected by the RTS.
 * @frame_txctl: &struct ieee80211_tx_info of the frame.
 *
 * If the RTS is generated in firmware, but the host system must provide
 * the duration field, the low-level driver uses this function to receive
 * the duration field value in little-endian byteorder.
 *
 * Return: The duration.
 */
__le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
			      struct ieee80211_vif *vif, size_t frame_len,
			      const struct ieee80211_tx_info *frame_txctl);

一般性的 duration_id - ieee80211_generic_frame_duration

定義在 net/mac80211/util.c 中,宣告在 include/net/mac80211.h 中:

/**
 * ieee80211_generic_frame_duration - Calculate the duration field for a frame
 * @hw: pointer obtained from ieee80211_alloc_hw().
 * @vif: &struct ieee80211_vif pointer from the add_interface callback.
 * @band: the band to calculate the frame duration on
 * @frame_len: the length of the frame.
 * @rate: the rate at which the frame is going to be transmitted.
 *
 * Calculate the duration field of some generic frame, given its
 * length and transmission rate (in 100kbps).
 *
 * Return: The duration.
 */
__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
					struct ieee80211_vif *vif,
					enum nl80211_band band,
					size_t frame_len,
					struct ieee80211_rate *rate);

主要的計算工作 - ieee80211_frame_duration

上述兩個函式主要的計算工作都是在 net/mac80211/util.c 當中的 ieee80211_frame_duration

int ieee80211_frame_duration(enum nl80211_band band, size_t len,
			     int rate, int erp, int short_preamble,
			     int shift)
{
	int dur;

	/* calculate duration (in microseconds, rounded up to next higher
	 * integer if it includes a fractional microsecond) to send frame of
	 * len bytes (does not include FCS) at the given rate. Duration will
	 * also include SIFS.
	 *
	 * rate is in 100 kbps, so divident is multiplied by 10 in the
	 * DIV_ROUND_UP() operations.
	 *
	 * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and
	 * is assumed to be 0 otherwise.
	 */

	if (band == NL80211_BAND_5GHZ || erp) {
		/*
		 * OFDM:
		 *
		 * N_DBPS = DATARATE x 4
		 * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS)
		 *	(16 = SIGNAL time, 6 = tail bits)
		 * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
		 *
		 * T_SYM = 4 usec
		 * 802.11a - 18.5.2: aSIFSTime = 16 usec
		 * 802.11g - 19.8.4: aSIFSTime = 10 usec +
		 *	signal ext = 6 usec
		 */
		dur = 16; /* SIFS + signal ext */
		dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */
		dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */

		/* IEEE 802.11-2012 18.3.2.4: all values above are:
		 *  * times 4 for 5 MHz
		 *  * times 2 for 10 MHz
		 */
		dur *= 1 << shift;

		/* rates should already consider the channel bandwidth,
		 * don't apply divisor again.
		 */
		dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
					4 * rate); /* T_SYM x N_SYM */
	} else {
		/*
		 * 802.11b or 802.11g with 802.11b compatibility:
		 * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime +
		 * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0.
		 *
		 * 802.11 (DS): 15.3.3, 802.11b: 18.3.4
		 * aSIFSTime = 10 usec
		 * aPreambleLength = 144 usec or 72 usec with short preamble
		 * aPLCPHeaderLength = 48 usec or 24 usec with short preamble
		 */
		dur = 10; /* aSIFSTime = 10 usec */
		dur += short_preamble ? (72 + 24) : (144 + 48);

		dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate);
	}

	return dur;
}