요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
===========================
How to use radiotap headers
===========================
Pointer to the radiotap include file
------------------------------------
Radiotap headers are variable-length and extensible, you can get most of the
information you need to know on them from::
./include/net/ieee80211_radiotap.h
This document gives an overview and warns on some corner cases.
Structure of the header
-----------------------
There is a fixed portion at the start which contains a u32 bitmap that defines
if the possible argument associated with that bit is present or not. So if b0
of the it_present member of ieee80211_radiotap_header is set, it means that
the header for argument index 0 (IEEE80211_RADIOTAP_TSFT) is present in the
argument area.
::
< 8-byte ieee80211_radiotap_header >
[ <possible argument bitmap extensions ... > ]
[ <argument> ... ]
At the moment there are only 13 possible argument indexes defined, but in case
we run out of space in the u32 it_present member, it is defined that b31 set
indicates that there is another u32 bitmap following (shown as "possible
argument bitmap extensions..." above), and the start of the arguments is moved
forward 4 bytes each time.
Note also that the it_len member __le16 is set to the total number of bytes
covered by the ieee80211_radiotap_header and any arguments following.
Requirements for arguments
--------------------------
After the fixed part of the header, the arguments follow for each argument
index whose matching bit is set in the it_present member of
ieee80211_radiotap_header.
- the arguments are all stored little-endian!
- the argument payload for a given argument index has a fixed size. So
IEEE80211_RADIOTAP_TSFT being present always indicates an 8-byte argument is
present. See the comments in ./include/net/ieee80211_radiotap.h for a nice
breakdown of all the argument sizes
- the arguments must be aligned to a boundary of the argument size using
padding. So a u16 argument must start on the next u16 boundary if it isn't
already on one, a u32 must start on the next u32 boundary and so on.
- "alignment" is relative to the start of the ieee80211_radiotap_header, ie,
the first byte of the radiotap header. The absolute alignment of that first
byte isn't defined. So even if the whole radiotap header is starting at, eg,
address 0x00000003, still the first byte of the radiotap header is treated as
0 for alignment purposes.
- the above point that there may be no absolute alignment for multibyte
entities in the fixed radiotap header or the argument region means that you
have to take special evasive action when trying to access these multibyte
entities. Some arches like Blackfin cannot deal with an attempt to
dereference, eg, a u16 pointer that is pointing to an odd address. Instead
you have to use a kernel API get_unaligned() to dereference the pointer,
which will do it bytewise on the arches that require that.
- The arguments for a given argument index can be a compound of multiple types
together. For example IEEE80211_RADIOTAP_CHANNEL has an argument payload
consisting of two u16s of total length 4. When this happens, the padding
rule is applied dealing with a u16, NOT dealing with a 4-byte single entity.
Example valid radiotap header
-----------------------------
::
0x00, 0x00, // <-- radiotap version + pad byte
0x0b, 0x00, // <- radiotap header length
0x04, 0x0c, 0x00, 0x00, // <-- bitmap
0x6c, // <-- rate (in 500kHz units)
0x0c, //<-- tx power
0x01 //<-- antenna
Using the Radiotap Parser
-------------------------
If you are having to parse a radiotap struct, you can radically simplify the
job by using the radiotap parser that lives in net/wireless/radiotap.c and has
its prototypes available in include/net/cfg80211.h. You use it like this::
#include <net/cfg80211.h>
/* buf points to the start of the radiotap header part */
int MyFunction(u8 * buf, int buflen)
{
int pkt_rate_100kHz = 0, antenna = 0, pwr = 0;
struct ieee80211_radiotap_iterator iterator;
int ret = ieee80211_radiotap_iterator_init(&iterator, buf, buflen);
while (!ret) {
ret = ieee80211_radiotap_iterator_next(&iterator);
if (ret)
continue;
/* see if this argument is something we can use */
switch (iterator.this_arg_index) {
/*
* You must take care when dereferencing iterator.this_arg
* for multibyte types... the pointer is not aligned. Use
* get_unaligned((type *)iterator.this_arg) to dereference
* iterator.this_arg for type "type" safely on all arches.
*/
case IEEE80211_RADIOTAP_RATE:
/* radiotap "rate" u8 is in
* 500kbps units, eg, 0x02=1Mbps
*/
pkt_rate_100kHz = (*iterator.this_arg) * 5;
break;
case IEEE80211_RADIOTAP_ANTENNA:
/* radiotap uses 0 for 1st ant */
antenna = *iterator.this_arg);
break;
case IEEE80211_RADIOTAP_DBM_TX_POWER:
pwr = *iterator.this_arg;
break;
default:
break;
}
} /* while more rt headers */
if (ret != -ENOENT)
return TXRX_DROP;
/* discard the radiotap header part */
buf += iterator.max_length;
buflen -= iterator.max_length;
...
}
Andy Green <[email protected]>
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
정의 header와 문서 범위
1-17Radiotap header는 길이가 가변적이고 확장할 수 있는 metadata header입니다. Field 정의, argument index, 크기 같은 대부분의 규격 정보는 kernel source의 `./include/net/ieee80211_radiotap.h`에 있습니다.
이 문서는 include file을 반복해서 열거하기보다 전체 layout과 parser 사용법을 개괄하고, padding·relative alignment·unaligned access처럼 구현자가 놓치기 쉬운 corner case를 경고합니다.
정의와 사용 지침의 위치입니다.
.. SPDX-License-Identifier: GPL-2.0
===========================
How to use radiotap headers
===========================
Pointer to the radiotap include file
------------------------------------
Radiotap headers are variable-length and extensible, you can get most of the
information you need to know on them from::
./include/net/ieee80211_radiotap.h
This document gives an overview and warns on some corner cases.
고정 header, present bitmap과 전체 길이
18-42Header 시작에는 8-byte 고정 `ieee80211_radiotap_header`가 있습니다. 그 안의 `it_present`는 32-bit bitmap이며, 각 bit는 대응하는 argument index가 argument area에 존재하는지를 표시합니다. 예를 들어 bit 0(`b0`)이 설정되면 index 0인 `IEEE80211_RADIOTAP_TSFT` argument가 있다는 뜻입니다.
고정 header 뒤에는 필요할 때 하나 이상의 bitmap extension이 오고, 그 뒤에 실제 argument가 index 순서대로 배치됩니다. 문서 작성 당시 정의된 argument index는 13개뿐이지만 `it_present`의 bit 31(`b31`)은 다음 32-bit bitmap이 이어진다는 extension marker입니다. Bitmap이 하나 늘 때마다 argument 시작 위치도 4 byte 뒤로 이동합니다.
`it_len`은 little-endian 16-bit 값인 `__le16`이며 고정 `ieee80211_radiotap_header`, bitmap extension, 뒤따르는 모든 argument와 padding을 포함한 radiotap header 전체 byte 수를 나타냅니다.
원문의 3단 ASCII layout을 확장 가능한 구조로 재구성했습니다.
고정 header가 argument 해석에 제공하는 정보입니다.
Structure of the header
-----------------------
There is a fixed portion at the start which contains a u32 bitmap that defines
if the possible argument associated with that bit is present or not. So if b0
of the it_present member of ieee80211_radiotap_header is set, it means that
the header for argument index 0 (IEEE80211_RADIOTAP_TSFT) is present in the
argument area.
::
< 8-byte ieee80211_radiotap_header >
[ <possible argument bitmap extensions ... > ]
[ <argument> ... ]
At the moment there are only 13 possible argument indexes defined, but in case
we run out of space in the u32 it_present member, it is defined that b31 set
indicates that there is another u32 bitmap following (shown as "possible
argument bitmap extensions..." above), and the start of the arguments is moved
forward 4 bytes each time.
Note also that the it_len member __le16 is set to the total number of bytes
covered by the ieee80211_radiotap_header and any arguments following.
Argument endian·크기·정렬 규칙
43-80고정 header 뒤에는 `it_present`에서 bit가 설정된 index의 argument만 이어집니다. 모든 argument는 little-endian으로 저장되며, 각 argument index의 payload 크기는 고정입니다. 따라서 `IEEE80211_RADIOTAP_TSFT`가 있으면 언제나 8-byte argument가 있다는 뜻입니다. 전체 index별 크기는 `ieee80211_radiotap.h` 주석에서 확인합니다.
각 argument는 자신의 정렬 단위 경계에서 시작해야 하며 필요한 만큼 padding을 둡니다. `u16`은 다음 2-byte 경계, `u32`는 다음 4-byte 경계에 놓습니다. 이 정렬은 실제 memory address가 아니라 `ieee80211_radiotap_header`의 첫 byte를 offset 0으로 삼아 계산합니다. Header 자체가 예를 들어 절대 주소 `0x00000003`에서 시작하더라도 radiotap 내부 offset 0을 기준으로 정렬합니다.
그러므로 실제 memory에서는 multibyte field가 unaligned address에 놓일 수 있습니다. Blackfin 같은 architecture는 홀수 주소를 가리키는 `u16 *`를 직접 역참조할 수 없으므로 fixed header와 argument area의 multibyte 값에 일반 pointer dereference를 사용하면 안 됩니다. Kernel의 `get_unaligned()`를 사용하면 필요한 architecture에서 byte 단위로 안전하게 읽습니다.
하나의 argument가 여러 type의 compound field일 수도 있습니다. `IEEE80211_RADIOTAP_CHANNEL`은 `u16` 두 개로 총 4 byte이지만, 정렬 단위는 4-byte entity가 아니라 구성 원소인 `u16`의 2 byte입니다. Padding을 계산할 때 argument 총길이와 alignment type을 혼동하지 않아야 합니다.
Parser와 직접 접근 코드가 지켜야 할 규칙입니다.
절대 시작 주소와 무관하게 내부 offset으로 padding을 계산합니다.
Requirements for arguments
--------------------------
After the fixed part of the header, the arguments follow for each argument
index whose matching bit is set in the it_present member of
ieee80211_radiotap_header.
- the arguments are all stored little-endian!
- the argument payload for a given argument index has a fixed size. So
IEEE80211_RADIOTAP_TSFT being present always indicates an 8-byte argument is
present. See the comments in ./include/net/ieee80211_radiotap.h for a nice
breakdown of all the argument sizes
- the arguments must be aligned to a boundary of the argument size using
padding. So a u16 argument must start on the next u16 boundary if it isn't
already on one, a u32 must start on the next u32 boundary and so on.
- "alignment" is relative to the start of the ieee80211_radiotap_header, ie,
the first byte of the radiotap header. The absolute alignment of that first
byte isn't defined. So even if the whole radiotap header is starting at, eg,
address 0x00000003, still the first byte of the radiotap header is treated as
0 for alignment purposes.
- the above point that there may be no absolute alignment for multibyte
entities in the fixed radiotap header or the argument region means that you
have to take special evasive action when trying to access these multibyte
entities. Some arches like Blackfin cannot deal with an attempt to
dereference, eg, a u16 pointer that is pointing to an odd address. Instead
you have to use a kernel API get_unaligned() to dereference the pointer,
which will do it bytewise on the arches that require that.
- The arguments for a given argument index can be a compound of multiple types
together. For example IEEE80211_RADIOTAP_CHANNEL has an argument payload
consisting of two u16s of total length 4. When this happens, the padding
rule is applied dealing with a u16, NOT dealing with a 4-byte single entity.
11-byte 유효 header 예제
81-93예제 header는 version과 pad byte로 시작하고 `it_len` 값 `0x000b`로 전체 길이가 11 byte임을 표시합니다. 4-byte bitmap `0x00000c04` 뒤에는 rate, Tx power, antenna argument가 각각 1 byte씩 옵니다.
Rate 값 `0x6c`는 500 kbps 단위이고, 뒤의 `0x0c`는 transmit power, `0x01`은 antenna를 나타냅니다. 이 세 argument는 모두 1-byte이므로 사이에 추가 alignment padding이 필요하지 않습니다.
원문의 byte 배열을 offset과 의미가 보이는 표로 재구성했습니다.
Example valid radiotap header
-----------------------------
::
0x00, 0x00, // <-- radiotap version + pad byte
0x0b, 0x00, // <- radiotap header length
0x04, 0x0c, 0x00, 0x00, // <-- bitmap
0x6c, // <-- rate (in 500kHz units)
0x0c, //<-- tx power
0x01 //<-- antenna
Kernel iterator parser 사용
94-159Radiotap structure를 직접 parse해야 한다면 `net/wireless/radiotap.c`의 kernel parser를 사용하면 작업이 크게 단순해집니다. Prototype은 `include/net/cfg80211.h`에 있으며 먼저 이 header를 include합니다.
예제 `MyFunction()`은 radiotap 시작을 가리키는 `buf`와 길이 `buflen`을 받아 `struct ieee80211_radiotap_iterator`를 준비합니다. `ieee80211_radiotap_iterator_init(&iterator, buf, buflen)`으로 초기화한 뒤 오류가 없는 동안 `ieee80211_radiotap_iterator_next()`를 호출해 present argument를 하나씩 순회합니다.
각 iteration에서는 `iterator.this_arg_index`로 field 종류를 판별하고 `iterator.this_arg`에서 값을 읽습니다. `IEEE80211_RADIOTAP_RATE`는 500 kbps 단위의 `u8`이므로 예제는 100 kHz 단위 값으로 바꾸기 위해 5를 곱합니다. `IEEE80211_RADIOTAP_ANTENNA`는 첫 antenna를 0으로 번호 매기며 `IEEE80211_RADIOTAP_DBM_TX_POWER`는 transmit power를 제공합니다.
`iterator.this_arg`는 정렬된 pointer라고 가정할 수 없습니다. 예제의 세 field는 `u8`이라 직접 읽지만 multibyte type은 반드시 `get_unaligned((type *)iterator.this_arg)` 형태로 역참조해야 모든 architecture에서 안전합니다. 원문 sample code와 주석은 source block에 그대로 보존되어 있으며, antenna 대입문의 추가 닫는 괄호도 원문 그대로입니다.
Iterator가 모든 field를 정상적으로 소비하면 `-ENOENT`로 끝납니다. 다른 error면 packet을 `TXRX_DROP` 처리합니다. 성공 시 `iterator.max_length`만큼 `buf`를 앞으로 이동하고 `buflen`에서 같은 길이를 빼 radiotap header를 버린 뒤 실제 frame payload를 처리합니다. 문서의 저자는 Andy Green입니다.
초기화부터 payload 진입까지의 parser control flow입니다.
예제에서 사용하는 iterator state입니다.
Using the Radiotap Parser
-------------------------
If you are having to parse a radiotap struct, you can radically simplify the
job by using the radiotap parser that lives in net/wireless/radiotap.c and has
its prototypes available in include/net/cfg80211.h. You use it like this::
#include <net/cfg80211.h>
/* buf points to the start of the radiotap header part */
int MyFunction(u8 * buf, int buflen)
{
int pkt_rate_100kHz = 0, antenna = 0, pwr = 0;
struct ieee80211_radiotap_iterator iterator;
int ret = ieee80211_radiotap_iterator_init(&iterator, buf, buflen);
while (!ret) {
ret = ieee80211_radiotap_iterator_next(&iterator);
if (ret)
continue;
/* see if this argument is something we can use */
switch (iterator.this_arg_index) {
/*
* You must take care when dereferencing iterator.this_arg
* for multibyte types... the pointer is not aligned. Use
* get_unaligned((type *)iterator.this_arg) to dereference
* iterator.this_arg for type "type" safely on all arches.
*/
case IEEE80211_RADIOTAP_RATE:
/* radiotap "rate" u8 is in
* 500kbps units, eg, 0x02=1Mbps
*/
pkt_rate_100kHz = (*iterator.this_arg) * 5;
break;
case IEEE80211_RADIOTAP_ANTENNA:
/* radiotap uses 0 for 1st ant */
antenna = *iterator.this_arg);
break;
case IEEE80211_RADIOTAP_DBM_TX_POWER:
pwr = *iterator.this_arg;
break;
default:
break;
}
} /* while more rt headers */
if (ret != -ENOENT)
return TXRX_DROP;
/* discard the radiotap header part */
buf += iterator.max_length;
buflen -= iterator.max_length;
...
}
Andy Green <[email protected]>
요약·해설
radiotap-headers.rst:1-159Radiotap은 8-byte 고정 header의 present bitmap으로 뒤따르는 variable argument를 선언합니다. Argument는 little-endian이고 header 시작을 기준으로 type 크기에 맞춰 상대 정렬하며, 실제 주소가 unaligned일 수 있으므로 multibyte access에는 `get_unaligned()`가 필요합니다. Kernel iterator API를 쓰면 extension bitmap, padding과 field 순회를 안전하게 처리할 수 있습니다.
Header 선언에서 payload 진입까지입니다.