← Documents Documentation/networking/radiotap-headers.rst GitHub 원문 ↗

Linux 6.18.37 · Networking

Radiotap header 사용법

가변 길이 radiotap header의 present bitmap, argument 정렬 규칙과 kernel iterator parser 사용법입니다.

Source pathDocumentation/networking/radiotap-headers.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

radiotap-headers.rst:1-159

Radiotap은 8-byte 고정 header의 present bitmap으로 뒤따르는 variable argument를 선언합니다. Argument는 little-endian이고 header 시작을 기준으로 type 크기에 맞춰 상대 정렬하며, 실제 주소가 unaligned일 수 있으므로 multibyte access에는 `get_unaligned()`가 필요합니다. Kernel iterator API를 쓰면 extension bitmap, padding과 field 순회를 안전하게 처리할 수 있습니다.

Radiotap 해석 경로
Fixed header + it_present선택적 bitmap extension정렬된 argument arearadiotap iteratoriterator.max_length 뒤 frame payload

Header 선언에서 payload 진입까지입니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ===========================
4 How to use radiotap headers
5 ===========================
6
7 Pointer to the radiotap include file
8 ------------------------------------
9
10 Radiotap headers are variable-length and extensible, you can get most of the
11 information you need to know on them from::
12
13 ./include/net/ieee80211_radiotap.h
14
15 This document gives an overview and warns on some corner cases.
16
17
18 Structure of the header
19 -----------------------
20
21 There is a fixed portion at the start which contains a u32 bitmap that defines
22 if the possible argument associated with that bit is present or not. So if b0
23 of the it_present member of ieee80211_radiotap_header is set, it means that
24 the header for argument index 0 (IEEE80211_RADIOTAP_TSFT) is present in the
25 argument area.
26
27 ::
28
29 < 8-byte ieee80211_radiotap_header >
30 [ <possible argument bitmap extensions ... > ]
31 [ <argument> ... ]
32
33 At the moment there are only 13 possible argument indexes defined, but in case
34 we run out of space in the u32 it_present member, it is defined that b31 set
35 indicates that there is another u32 bitmap following (shown as "possible
36 argument bitmap extensions..." above), and the start of the arguments is moved
37 forward 4 bytes each time.
38
39 Note also that the it_len member __le16 is set to the total number of bytes
40 covered by the ieee80211_radiotap_header and any arguments following.
41
42
43 Requirements for arguments
44 --------------------------
45
46 After the fixed part of the header, the arguments follow for each argument
47 index whose matching bit is set in the it_present member of
48 ieee80211_radiotap_header.
49
50 - the arguments are all stored little-endian!
51
52 - the argument payload for a given argument index has a fixed size. So
53 IEEE80211_RADIOTAP_TSFT being present always indicates an 8-byte argument is
54 present. See the comments in ./include/net/ieee80211_radiotap.h for a nice
55 breakdown of all the argument sizes
56
57 - the arguments must be aligned to a boundary of the argument size using
58 padding. So a u16 argument must start on the next u16 boundary if it isn't
59 already on one, a u32 must start on the next u32 boundary and so on.
60
61 - "alignment" is relative to the start of the ieee80211_radiotap_header, ie,
62 the first byte of the radiotap header. The absolute alignment of that first
63 byte isn't defined. So even if the whole radiotap header is starting at, eg,
64 address 0x00000003, still the first byte of the radiotap header is treated as
65 0 for alignment purposes.
66
67 - the above point that there may be no absolute alignment for multibyte
68 entities in the fixed radiotap header or the argument region means that you
69 have to take special evasive action when trying to access these multibyte
70 entities. Some arches like Blackfin cannot deal with an attempt to
71 dereference, eg, a u16 pointer that is pointing to an odd address. Instead
72 you have to use a kernel API get_unaligned() to dereference the pointer,
73 which will do it bytewise on the arches that require that.
74
75 - The arguments for a given argument index can be a compound of multiple types
76 together. For example IEEE80211_RADIOTAP_CHANNEL has an argument payload
77 consisting of two u16s of total length 4. When this happens, the padding
78 rule is applied dealing with a u16, NOT dealing with a 4-byte single entity.
79
80
81 Example valid radiotap header
82 -----------------------------
83
84 ::
85
86 0x00, 0x00, // <-- radiotap version + pad byte
87 0x0b, 0x00, // <- radiotap header length
88 0x04, 0x0c, 0x00, 0x00, // <-- bitmap
89 0x6c, // <-- rate (in 500kHz units)
90 0x0c, //<-- tx power
91 0x01 //<-- antenna
92
93
94 Using the Radiotap Parser
95 -------------------------
96
97 If you are having to parse a radiotap struct, you can radically simplify the
98 job by using the radiotap parser that lives in net/wireless/radiotap.c and has
99 its prototypes available in include/net/cfg80211.h. You use it like this::
100
101 #include <net/cfg80211.h>
102
103 /* buf points to the start of the radiotap header part */
104
105 int MyFunction(u8 * buf, int buflen)
106 {
107 int pkt_rate_100kHz = 0, antenna = 0, pwr = 0;
108 struct ieee80211_radiotap_iterator iterator;
109 int ret = ieee80211_radiotap_iterator_init(&iterator, buf, buflen);
110
111 while (!ret) {
112
113 ret = ieee80211_radiotap_iterator_next(&iterator);
114
115 if (ret)
116 continue;
117
118 /* see if this argument is something we can use */
119
120 switch (iterator.this_arg_index) {
121 /*
122 * You must take care when dereferencing iterator.this_arg
123 * for multibyte types... the pointer is not aligned. Use
124 * get_unaligned((type *)iterator.this_arg) to dereference
125 * iterator.this_arg for type "type" safely on all arches.
126 */
127 case IEEE80211_RADIOTAP_RATE:
128 /* radiotap "rate" u8 is in
129 * 500kbps units, eg, 0x02=1Mbps
130 */
131 pkt_rate_100kHz = (*iterator.this_arg) * 5;
132 break;
133
134 case IEEE80211_RADIOTAP_ANTENNA:
135 /* radiotap uses 0 for 1st ant */
136 antenna = *iterator.this_arg);
137 break;
138
139 case IEEE80211_RADIOTAP_DBM_TX_POWER:
140 pwr = *iterator.this_arg;
141 break;
142
143 default:
144 break;
145 }
146 } /* while more rt headers */
147
148 if (ret != -ENOENT)
149 return TXRX_DROP;
150
151 /* discard the radiotap header part */
152 buf += iterator.max_length;
153 buflen -= iterator.max_length;
154
155 ...
156
157 }
158
159 Andy Green <[email protected]>
160

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

정의 header와 문서 범위

1-17

Radiotap header는 길이가 가변적이고 확장할 수 있는 metadata header입니다. Field 정의, argument index, 크기 같은 대부분의 규격 정보는 kernel source의 `./include/net/ieee80211_radiotap.h`에 있습니다.

이 문서는 include file을 반복해서 열거하기보다 전체 layout과 parser 사용법을 개괄하고, padding·relative alignment·unaligned access처럼 구현자가 놓치기 쉬운 corner case를 경고합니다.

Radiotap 참고 자료
경로역할
include/net/ieee80211_radiotap.hField index, 고정 크기와 structure 정의
Documentation/networking/radiotap-headers.rstLayout, 정렬 규칙과 parser 사용 개요

정의와 사용 지침의 위치입니다.

.. 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-42

Header 시작에는 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 수를 나타냅니다.

Radiotap header 구조
8-byte ieee80211_radiotap_header선택적 u32 present bitmap extension들Present bit가 지정한 argument들Frame payload

원문의 3단 ASCII layout을 확장 가능한 구조로 재구성했습니다.

핵심 고정 field
Field / bit의미
it_present b0..b30대응 argument index의 존재 여부
it_present b31다음 u32 bitmap 존재
it_len (__le16)모든 extension·argument를 포함한 전체 radiotap 길이

고정 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을 혼동하지 않아야 합니다.

Radiotap argument 규칙
규칙적용
Byte order모든 argument는 little-endian
Payload sizeArgument index마다 고정
Alignment originRadiotap header 첫 byte = offset 0
Padding unitArgument type 크기; compound는 구성 원소 type
Multibyte accessget_unaligned() 사용

Parser와 직접 접근 코드가 지켜야 할 규칙입니다.

상대 정렬 예시
절대 주소 0x00000003Radiotap 내부 offset 0u16 argument는 내부 2-byte 경계get_unaligned()로 읽기

절대 시작 주소와 무관하게 내부 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이 필요하지 않습니다.

예제 radiotap byte layout
OffsetBytes의미
0..100 00Radiotap version + pad
2..30b 00Header length = 11 (little-endian)
4..704 0c 00 00Present bitmap
86cRate, 500 kbps 단위
90cTx power
1001Antenna

원문의 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-159

Radiotap 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입니다.

Radiotap iterator
iterator_init(buf, buflen)iterator_next 반복this_arg_index switch-ENOENT 정상 종료buf += max_lengthFrame payload 처리
Iterator error != -ENOENTTXRX_DROP

초기화부터 payload 진입까지의 parser control flow입니다.

Iterator field
Member용도
this_arg_index현재 argument의 radiotap index
this_arg현재 payload pointer; multibyte는 get_unaligned()
max_length검증된 radiotap header 전체 길이

예제에서 사용하는 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]>