]> git.ipfire.org Git - people/jschlag/network.git/blob - src/libnetwork/phy.c
libnetwork: Don't fail when wireless devices are not supported by nl80211
[people/jschlag/network.git] / src / libnetwork / phy.c
1 /*#############################################################################
2 # #
3 # IPFire.org - A linux based firewall #
4 # Copyright (C) 2018 IPFire Network Development Team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <errno.h>
22 #include <linux/nl80211.h>
23 #include <netlink/attr.h>
24 #include <netlink/genl/genl.h>
25 #include <netlink/msg.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 #include <network/libnetwork.h>
31 #include <network/logging.h>
32 #include <network/phy.h>
33 #include "libnetwork-private.h"
34
35 struct network_phy {
36 struct network_ctx* ctx;
37 int refcount;
38
39 int index;
40 char* name;
41
42 unsigned int ht_caps;
43 };
44
45 static int phy_get_index(const char* name) {
46 char path[1024];
47 char index[8];
48
49 snprintf(path, sizeof(path), "/sys/class/ieee80211/%s/index", name);
50
51 FILE* f = fopen(path, "r");
52 if (!f)
53 return -1;
54
55 int p = fread(index, 1, sizeof(index), f);
56 if (p < 0) {
57 fclose(f);
58 return -1;
59 }
60
61 // Terminate buffer
62 index[p] = '\0';
63 fclose(f);
64
65 return atoi(index);
66 }
67
68 static void phy_parse_ht_capabilities(struct network_phy* phy, __u16 caps) {
69 // RX LDCP
70 if (caps & BIT(0))
71 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_LDCP;
72
73 // HT40
74 if (caps & BIT(1))
75 phy->ht_caps |= NETWORK_PHY_HT_CAP_HT40;
76
77 // Static/Dynamic SM Power Save
78 switch ((caps >> 2) & 0x3) {
79 case 0:
80 phy->ht_caps |= NETWORK_PHY_HT_CAP_SMPS_STATIC;
81 break;
82
83 case 1:
84 phy->ht_caps |= NETWORK_PHY_HT_CAP_SMPS_DYNAMIC;
85 break;
86
87 default:
88 break;
89 }
90
91 // RX Greenfield
92 if (caps & BIT(4))
93 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_GF;
94
95 // RX HT20 Short GI
96 if (caps & BIT(5))
97 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_HT20_SGI;
98
99 // RX HT40 Short GI
100 if (caps & BIT(6))
101 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_HT40_SGI;
102
103 // TX STBC
104 if (caps & BIT(7))
105 phy->ht_caps |= NETWORK_PHY_HT_CAP_TX_STBC;
106
107 // RX STBC
108 switch ((caps >> 8) & 0x3) {
109 case 1:
110 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC1;
111 break;
112
113 case 2:
114 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC2;
115 break;
116
117 case 3:
118 phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC3;
119 break;
120
121 default:
122 break;
123 }
124
125 // HT Delayed Block ACK
126 if (caps & BIT(10))
127 phy->ht_caps |= NETWORK_PHY_HT_CAP_DELAYED_BA;
128
129 // Max AMSDU length 7935
130 if (caps & BIT(11))
131 phy->ht_caps |= NETWORK_PHY_HT_CAP_MAX_AMSDU_7935;
132
133 // DSSS/CCK HT40
134 if (caps & BIT(12))
135 phy->ht_caps |= NETWORK_PHY_HT_CAP_DSSS_CCK_HT40;
136
137 // Bit 13 is reserved
138
139 // HT40 Intolerant
140 if (caps & BIT(14))
141 phy->ht_caps |= NETWORK_PHY_HT_CAP_HT40_INTOLERANT;
142
143 // L-SIG TXOP protection
144 if (caps & BIT(15))
145 phy->ht_caps |= NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT;
146 }
147
148 static int phy_parse_info(struct nl_msg* msg, void* data) {
149 struct network_phy* phy = data;
150
151 struct nlattr* attrs[NL80211_ATTR_MAX + 1];
152 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
153
154 nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
155 genlmsg_attrlen(gnlh, 0), NULL);
156
157 if (attrs[NL80211_ATTR_WIPHY_BANDS]) {
158 struct nlattr* nl_band;
159 int i;
160
161 nla_for_each_nested(nl_band, attrs[NL80211_ATTR_WIPHY_BANDS], i) {
162 struct nlattr* band_attrs[NL80211_BAND_ATTR_MAX + 1];
163 nla_parse(band_attrs, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
164 nla_len(nl_band), NULL);
165
166 // HT Capabilities
167 if (band_attrs[NL80211_BAND_ATTR_HT_CAPA]) {
168 __u16 caps = nla_get_u16(band_attrs[NL80211_BAND_ATTR_HT_CAPA]);
169 phy_parse_ht_capabilities(phy, caps);
170 }
171 }
172 }
173
174 return NL_OK;
175 }
176
177 static int phy_get_info(struct network_phy* phy) {
178 DEBUG(phy->ctx, "Getting information for %s\n", phy->name);
179
180 struct nl_msg* msg = network_phy_make_netlink_message(phy, NL80211_CMD_GET_WIPHY, 0);
181 if (!msg)
182 return -1;
183
184 int r = network_send_netlink_message(phy->ctx, msg, phy_parse_info, phy);
185
186 // This is fine since some devices are not supported by NL80211
187 if (r == -ENODEV) {
188 DEBUG(phy->ctx, "Could not fetch information from kernel\n");
189 return 0;
190 }
191
192 return r;
193 }
194
195 static void network_phy_free(struct network_phy* phy) {
196 DEBUG(phy->ctx, "Releasing phy at %p\n", phy);
197
198 if (phy->name)
199 free(phy->name);
200
201 network_unref(phy->ctx);
202 free(phy);
203 }
204
205 NETWORK_EXPORT int network_phy_new(struct network_ctx* ctx, struct network_phy** phy,
206 const char* name) {
207 if (!name)
208 return -EINVAL;
209
210 int index = phy_get_index(name);
211 if (index < 0)
212 return -ENODEV;
213
214 struct network_phy* p = calloc(1, sizeof(*p));
215 if (!p)
216 return -ENOMEM;
217
218 // Initalise object
219 p->ctx = network_ref(ctx);
220 p->refcount = 1;
221
222 p->name = strdup(name);
223
224 // Load information from kernel
225 int r = phy_get_info(p);
226 if (r) {
227 ERROR(p->ctx, "Error getting PHY information from kernel\n");
228 network_phy_free(p);
229
230 return r;
231 }
232
233 DEBUG(p->ctx, "Allocated phy at %p\n", p);
234 *phy = p;
235 return 0;
236 }
237
238 NETWORK_EXPORT struct network_phy* network_phy_ref(struct network_phy* phy) {
239 if (!phy)
240 return NULL;
241
242 phy->refcount++;
243 return phy;
244 }
245
246 NETWORK_EXPORT struct network_phy* network_phy_unref(struct network_phy* phy) {
247 if (!phy)
248 return NULL;
249
250 if (--phy->refcount > 0)
251 return phy;
252
253 network_phy_free(phy);
254 return NULL;
255 }
256
257 struct nl_msg* network_phy_make_netlink_message(struct network_phy* phy,
258 enum nl80211_commands cmd, int flags) {
259 struct nl_msg* msg = network_make_netlink_message(phy->ctx, cmd, flags);
260 if (!msg)
261 return NULL;
262
263 // Set PHY index
264 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phy->index);
265
266 return msg;
267
268 nla_put_failure:
269 nlmsg_free(msg);
270
271 return NULL;
272 }
273
274 NETWORK_EXPORT int network_phy_has_ht_capability(struct network_phy* phy, const enum network_phy_ht_caps cap) {
275 return phy->ht_caps & cap;
276 }
277
278 static const char* network_phy_get_ht_capability_string(const enum network_phy_ht_caps cap) {
279 switch (cap) {
280 case NETWORK_PHY_HT_CAP_RX_LDCP:
281 return "[LDPC]";
282
283 case NETWORK_PHY_HT_CAP_HT40:
284 return "[HT40+][HT40-]";
285
286 case NETWORK_PHY_HT_CAP_SMPS_STATIC:
287 return "[SMPS-STATIC]";
288
289 case NETWORK_PHY_HT_CAP_SMPS_DYNAMIC:
290 return "[SMPS-DYNAMIC]";
291
292 case NETWORK_PHY_HT_CAP_RX_GF:
293 return "[GF]";
294
295 case NETWORK_PHY_HT_CAP_RX_HT20_SGI:
296 return "[SHORT-GI-20]";
297
298 case NETWORK_PHY_HT_CAP_RX_HT40_SGI:
299 return "[SHORT-GI-40]";
300
301 case NETWORK_PHY_HT_CAP_TX_STBC:
302 return "[TX-STBC]";
303
304 case NETWORK_PHY_HT_CAP_RX_STBC1:
305 return "[RX-STBC1]";
306
307 case NETWORK_PHY_HT_CAP_RX_STBC2:
308 return "[RX-STBC12]";
309
310 case NETWORK_PHY_HT_CAP_RX_STBC3:
311 return "[RX-STBC123]";
312
313 case NETWORK_PHY_HT_CAP_DELAYED_BA:
314 return "[DELAYED-BA]";
315
316 case NETWORK_PHY_HT_CAP_MAX_AMSDU_7935:
317 return "[MAX-AMSDU-7935]";
318
319 case NETWORK_PHY_HT_CAP_DSSS_CCK_HT40:
320 return "[DSSS_CCK-40]";
321
322 case NETWORK_PHY_HT_CAP_HT40_INTOLERANT:
323 return "[40-INTOLERANT]";
324
325 case NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT:
326 return "[LSIG-TXOP-PROT]";
327 }
328
329 return NULL;
330 }
331
332 NETWORK_EXPORT char* network_phy_list_ht_capabilities(struct network_phy* phy) {
333 char* buffer = malloc(1024);
334 *buffer = '\0';
335
336 char* p = buffer;
337 foreach_ht_cap(cap) {
338 if (network_phy_has_ht_capability(phy, cap)) {
339 const char* cap_str = network_phy_get_ht_capability_string(cap);
340
341 if (cap_str)
342 p = strncat(p, cap_str, 1024 - 1);
343 }
344 }
345
346 return buffer;
347 }