# 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;
}
```