# 802.11 - RTS/CTS [TOC] ## 課程影片 ### 第 3C 講 IEEE 802.11 無線區域網路 (Wireless LAN) L03 3 {%youtube URKV_D54Hzw %} ## RTS/CTS 在送真的封包之前,先廣播一個 *request to send* 封包。這個封包不只有目的地會收到,發送者傳輸範圍內的其他裝置也會收到。而目的地如果順利收到這個 RTS 的封包,則必須廣播一個 *clear to send* (CTS) 封包,代表已經準備好接收封包。舉例來說,假定 $B$ 要送一個封包給 $C$: ![](https://i.imgur.com/kwXGLFA.png) 這時,$B$ 會廣播一個 *RTS* 封包: ![](https://i.imgur.com/Da3LPSx.png) 這個封包送出去之後,如果 $C$ 有收到這個 RTS 封包,就要也廣播一個 CTS 封包: ![](https://i.imgur.com/s2jBBtq.png) 並且規定:聽到 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*。 ## NAV (Network Allocation Vector) 節點會維護一個存放不同節點 *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`](https://elixir.bootlin.com/linux/latest/source/include/linux/ieee80211.h#L210) 中也有定義: ```c /* 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`](https://elixir.bootlin.com/linux/latest/source/include/linux/ieee80211.h#L1316) 中: ```c 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`](https://elixir.bootlin.com/linux/latest/source/include/linux/ieee80211.h#L1323) 中: ```c struct ieee80211_cts { __le16 frame_control; __le16 duration; u8 ra[ETH_ALEN]; } __packed __aligned(2); ``` ## 核心中的相關函式 ### 生成 RTS --- `ieee80211_rts_get` 宣告在 [`include/net/mac80211.h`](https://elixir.bootlin.com/linux/latest/source/include/net/mac80211.h#L5097) 中: ```c /** * 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`](https://elixir.bootlin.com/linux/latest/source/net/mac80211/tx.c#L5437): ```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`](https://elixir.bootlin.com/linux/latest/source/net/mac80211/util.c#L206) 中,宣告在 [`include/net/mac80211.h`](https://elixir.bootlin.com/linux/latest/source/include/net/mac80211.h#L5102): ```c /** * 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`](https://elixir.bootlin.com/linux/latest/source/net/mac80211/util.c#L179) 中,宣告在 [`include/net/mac80211.h`](https://elixir.bootlin.com/linux/latest/source/include/net/mac80211.h#L5170) 中: ```c /** * 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`](https://elixir.bootlin.com/linux/latest/source/net/mac80211/util.c#L111): ```c 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; } ```