2 * iwinfo - Wireless Information Library - Shared utility routines
4 * Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
6 * The iwinfo library is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
10 * The iwinfo library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with the iwinfo library. If not, see http://www.gnu.org/licenses/.
18 * The signal handling code is derived from the official madwifi tools,
19 * wlanconfig.c in particular. The encryption property handling was
20 * inspired by the hostapd madwifi driver.
23 #include "iwinfo/utils.h"
26 static int ioctl_socket
= -1;
27 struct uci_context
*uci_ctx
= NULL
;
29 static int iwinfo_ioctl_socket(void)
32 if (ioctl_socket
== -1)
34 ioctl_socket
= socket(AF_INET
, SOCK_DGRAM
, 0);
35 fcntl(ioctl_socket
, F_SETFD
, fcntl(ioctl_socket
, F_GETFD
) | FD_CLOEXEC
);
41 int iwinfo_ioctl(int cmd
, void *ifr
)
43 int s
= iwinfo_ioctl_socket();
44 return ioctl(s
, cmd
, ifr
);
47 int iwinfo_dbm2mw(int in
)
54 for(k
= 0; k
< ip
; k
++) res
*= 10;
55 for(k
= 0; k
< fp
; k
++) res
*= LOG10_MAGIC
;
60 int iwinfo_mw2dbm(int in
)
62 double fin
= (double) in
;
80 static int iwinfo_bit(int value
, int max
)
84 if (max
> 31 || !(value
& ((1 << max
) - 1)))
87 for (i
= 0; i
< max
; i
++)
98 static const char * const iwinfo_name(int mask
, int max
, const char * const names
[])
100 int index
= iwinfo_bit(mask
, max
);
108 const char * const iwinfo_band_name(int mask
)
110 return iwinfo_name(mask
, IWINFO_BAND_COUNT
, IWINFO_BAND_NAMES
);
113 const char * const iwinfo_htmode_name(int mask
)
115 return iwinfo_name(mask
, IWINFO_HTMODE_COUNT
, IWINFO_HTMODE_NAMES
);
118 uint32_t iwinfo_band2ghz(uint8_t band
)
135 uint8_t iwinfo_ghz2band(uint32_t ghz
)
140 return IWINFO_BAND_24
;
142 return IWINFO_BAND_5
;
144 return IWINFO_BAND_6
;
146 return IWINFO_BAND_60
;
152 size_t iwinfo_format_hwmodes(int modes
, char *buf
, size_t len
)
154 // bit numbers as per IWINFO_80211_*: ad ac ax a b be g n
155 const int order
[IWINFO_80211_COUNT
] = { 5, 4, 6, 0, 1, 7, 2, 3 };
161 if (!(modes
& ((1 << IWINFO_80211_COUNT
) - 1)))
164 for (i
= 0; i
< IWINFO_80211_COUNT
; i
++)
165 if (modes
& 1 << order
[i
])
166 res
+= snprintf(buf
+ res
, len
- res
, "%s/", IWINFO_80211_NAMES
[order
[i
]]);
177 int iwinfo_htmode_is_ht(int htmode
)
181 case IWINFO_HTMODE_HT20
:
182 case IWINFO_HTMODE_HT40
:
189 int iwinfo_htmode_is_vht(int htmode
)
193 case IWINFO_HTMODE_VHT20
:
194 case IWINFO_HTMODE_VHT40
:
195 case IWINFO_HTMODE_VHT80
:
196 case IWINFO_HTMODE_VHT80_80
:
197 case IWINFO_HTMODE_VHT160
:
204 int iwinfo_htmode_is_he(int htmode
)
208 case IWINFO_HTMODE_HE20
:
209 case IWINFO_HTMODE_HE40
:
210 case IWINFO_HTMODE_HE80
:
211 case IWINFO_HTMODE_HE80_80
:
212 case IWINFO_HTMODE_HE160
:
219 int iwinfo_htmode_is_eht(int htmode
)
223 case IWINFO_HTMODE_EHT20
:
224 case IWINFO_HTMODE_EHT40
:
225 case IWINFO_HTMODE_EHT80
:
226 case IWINFO_HTMODE_EHT80_80
:
227 case IWINFO_HTMODE_EHT160
:
228 case IWINFO_HTMODE_EHT320
:
235 int iwinfo_ifup(const char *ifname
)
239 strncpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
- 1);
241 if (iwinfo_ioctl(SIOCGIFFLAGS
, &ifr
))
244 ifr
.ifr_flags
|= (IFF_UP
| IFF_RUNNING
);
246 return !iwinfo_ioctl(SIOCSIFFLAGS
, &ifr
);
249 int iwinfo_ifdown(const char *ifname
)
253 strncpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
- 1);
255 if (iwinfo_ioctl(SIOCGIFFLAGS
, &ifr
))
258 ifr
.ifr_flags
&= ~(IFF_UP
| IFF_RUNNING
);
260 return !iwinfo_ioctl(SIOCSIFFLAGS
, &ifr
);
263 int iwinfo_ifmac(const char *ifname
)
267 strncpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
- 1);
269 if (iwinfo_ioctl(SIOCGIFHWADDR
, &ifr
))
272 ifr
.ifr_hwaddr
.sa_data
[0] |= 0x02;
273 ifr
.ifr_hwaddr
.sa_data
[1]++;
274 ifr
.ifr_hwaddr
.sa_data
[2]++;
276 return !iwinfo_ioctl(SIOCSIFHWADDR
, &ifr
);
279 void iwinfo_close(void)
281 if (ioctl_socket
> -1)
287 struct iwinfo_hardware_entry
* iwinfo_hardware(struct iwinfo_hardware_id
*id
)
290 char buf
[256] = { 0 };
291 static struct iwinfo_hardware_entry e
;
292 struct iwinfo_hardware_entry
*rv
= NULL
;
294 if (!(db
= fopen(IWINFO_HARDWARE_FILE
, "r")))
297 while (fgets(buf
, sizeof(buf
) - 1, db
) != NULL
)
302 memset(&e
, 0, sizeof(e
));
304 if (sscanf(buf
, "%hx %hx %hx %hx %hd %hd \"%63[^\"]\" \"%63[^\"]\"",
305 &e
.vendor_id
, &e
.device_id
,
306 &e
.subsystem_vendor_id
, &e
.subsystem_device_id
,
307 &e
.txpower_offset
, &e
.frequency_offset
,
308 e
.vendor_name
, e
.device_name
) != 8 &&
309 sscanf(buf
, "\"%127[^\"]\" %hd %hd \"%63[^\"]\" \"%63[^\"]\"",
310 e
.compatible
, &e
.txpower_offset
, &e
.frequency_offset
,
311 e
.vendor_name
, e
.device_name
) != 5)
314 if ((e
.vendor_id
!= 0xffff) && (e
.vendor_id
!= id
->vendor_id
))
317 if ((e
.device_id
!= 0xffff) && (e
.device_id
!= id
->device_id
))
320 if ((e
.subsystem_vendor_id
!= 0xffff) &&
321 (e
.subsystem_vendor_id
!= id
->subsystem_vendor_id
))
324 if ((e
.subsystem_device_id
!= 0xffff) &&
325 (e
.subsystem_device_id
!= id
->subsystem_device_id
))
328 if (strcmp(e
.compatible
, id
->compatible
))
339 int iwinfo_hardware_id_from_mtd(struct iwinfo_hardware_id
*id
)
348 if (!(mtd
= fopen("/proc/mtd", "r")))
351 while (fgets(buf
, sizeof(buf
), mtd
) != NULL
)
353 if (fscanf(mtd
, "mtd%d: %x %*x %127s", &off
, &len
, buf
) < 3 ||
354 (strcmp(buf
, "\"boardconfig\"") && strcmp(buf
, "\"EEPROM\"") &&
355 strcmp(buf
, "\"factory\"")))
369 snprintf(buf
, sizeof(buf
), "/dev/mtdblock%d", off
);
371 if ((fd
= open(buf
, O_RDONLY
)) < 0)
374 bc
= mmap(NULL
, len
, PROT_READ
, MAP_PRIVATE
|MAP_LOCKED
, fd
, 0);
376 if ((void *)bc
!= MAP_FAILED
)
381 for (off
= len
/ 2 - 0x800; off
>= 0; off
-= 0x800)
383 /* AR531X board data magic */
384 if ((bc
[off
] == 0x3533) && (bc
[off
+ 1] == 0x3131))
386 id
->vendor_id
= bc
[off
+ 0x7d];
387 id
->device_id
= bc
[off
+ 0x7c];
388 id
->subsystem_vendor_id
= bc
[off
+ 0x84];
389 id
->subsystem_device_id
= bc
[off
+ 0x83];
393 /* AR5416 EEPROM magic */
394 else if ((bc
[off
] == 0xA55A) || (bc
[off
] == 0x5AA5))
396 id
->vendor_id
= bc
[off
+ 0x0D];
397 id
->device_id
= bc
[off
+ 0x0E];
398 id
->subsystem_vendor_id
= bc
[off
+ 0x13];
399 id
->subsystem_device_id
= bc
[off
+ 0x14];
404 else if ((bc
[off
] == 0x3050) || (bc
[off
] == 0x5030) ||
405 (bc
[off
] == 0x3051) || (bc
[off
] == 0x5130) ||
406 (bc
[off
] == 0x3052) || (bc
[off
] == 0x5230) ||
407 (bc
[off
] == 0x3350) || (bc
[off
] == 0x5033) ||
408 (bc
[off
] == 0x3352) || (bc
[off
] == 0x5233) ||
409 (bc
[off
] == 0x3662) || (bc
[off
] == 0x6236) ||
410 (bc
[off
] == 0x3883) || (bc
[off
] == 0x8338) ||
411 (bc
[off
] == 0x5350) || (bc
[off
] == 0x5053))
414 id
->vendor_id
= 0x1814;
415 id
->subsystem_vendor_id
= 0x1814;
418 if (((bc
[off
] & 0xf0) == 0x30) ||
419 ((bc
[off
] & 0xff) == 0x53))
420 id
->device_id
= (bc
[off
] >> 8) | (bc
[off
] & 0x00ff) << 8;
422 id
->device_id
= bc
[off
];
424 /* subsystem from EEPROM_NIC_CONF0_RF_TYPE */
425 id
->subsystem_device_id
= (bc
[off
+ 0x1a] & 0x0f00) >> 8;
426 } else if ((bc
[off
] == 0x7620) || (bc
[off
] == 0x2076) ||
427 (bc
[off
] == 0x7628) || (bc
[off
] == 0x2876) ||
428 (bc
[off
] == 0x7688) || (bc
[off
] == 0x8876)) {
429 /* vendor: MediaTek */
430 id
->vendor_id
= 0x14c3;
431 id
->subsystem_vendor_id
= 0x14c3;
434 if ((bc
[off
] & 0xff) == 0x76)
435 id
->device_id
= (bc
[off
] >> 8) | (bc
[off
] & 0x00ff) << 8;
437 id
->device_id
= bc
[off
];
439 /* subsystem from EEPROM_NIC_CONF0_RF_TYPE */
440 id
->subsystem_device_id
= (bc
[off
+ 0x1a] & 0x0f00) >> 8;
449 return (id
->vendor_id
&& id
->device_id
) ? 0 : -1;
452 static void iwinfo_parse_rsn_cipher(uint8_t idx
, uint16_t *ciphers
)
457 *ciphers
|= IWINFO_CIPHER_NONE
;
461 *ciphers
|= IWINFO_CIPHER_WEP40
;
465 *ciphers
|= IWINFO_CIPHER_TKIP
;
472 *ciphers
|= IWINFO_CIPHER_CCMP
;
476 *ciphers
|= IWINFO_CIPHER_WEP104
;
480 *ciphers
|= IWINFO_CIPHER_GCMP
;
484 *ciphers
|= IWINFO_CIPHER_GCMP256
;
488 *ciphers
|= IWINFO_CIPHER_CCMP256
;
491 case 6: /* AES-128-CMAC */
492 case 7: /* No group addressed */
493 case 11: /* BIP-GMAC-128 */
494 case 12: /* BIP-GMAC-256 */
495 case 13: /* BIP-CMAC-256 */
500 void iwinfo_parse_rsn(struct iwinfo_crypto_entry
*c
, uint8_t *data
, uint8_t len
,
501 uint16_t defcipher
, uint8_t defauth
)
504 uint8_t wpa_version
= 0;
506 static unsigned char ms_oui
[3] = { 0x00, 0x50, 0xf2 };
507 static unsigned char ieee80211_oui
[3] = { 0x00, 0x0f, 0xac };
512 if (!memcmp(data
, ms_oui
, 3))
514 else if (!memcmp(data
, ieee80211_oui
, 3))
519 c
->group_ciphers
|= defcipher
;
520 c
->pair_ciphers
|= defcipher
;
521 c
->auth_suites
|= defauth
;
525 if (!memcmp(data
, ms_oui
, 3) || !memcmp(data
, ieee80211_oui
, 3))
526 iwinfo_parse_rsn_cipher(data
[3], &c
->group_ciphers
);
533 c
->pair_ciphers
|= defcipher
;
534 c
->auth_suites
|= defauth
;
538 count
= data
[0] | (data
[1] << 8);
539 if (2 + (count
* 4) > len
)
542 for (i
= 0; i
< count
; i
++)
543 if (!memcmp(data
+ 2 + (i
* 4), ms_oui
, 3) ||
544 !memcmp(data
+ 2 + (i
* 4), ieee80211_oui
, 3))
545 iwinfo_parse_rsn_cipher(data
[2 + (i
* 4) + 3], &c
->pair_ciphers
);
547 data
+= 2 + (count
* 4);
548 len
-= 2 + (count
* 4);
552 c
->auth_suites
|= defauth
;
556 count
= data
[0] | (data
[1] << 8);
557 if (2 + (count
* 4) > len
)
560 for (i
= 0; i
< count
; i
++)
562 if (!memcmp(data
+ 2 + (i
* 4), ms_oui
, 3) ||
563 !memcmp(data
+ 2 + (i
* 4), ieee80211_oui
, 3))
565 switch (data
[2 + (i
* 4) + 3])
567 case 1: /* IEEE 802.1x */
568 c
->wpa_version
|= wpa_version
;
569 c
->auth_suites
|= IWINFO_KMGMT_8021x
;
573 c
->wpa_version
|= wpa_version
;
574 c
->auth_suites
|= IWINFO_KMGMT_PSK
;
577 case 3: /* FT/IEEE 802.1X */
579 case 5: /* IEEE 802.1X/SHA-256 */
580 case 6: /* PSK/SHA-256 */
581 case 7: /* TPK Handshake */
586 c
->auth_suites
|= IWINFO_KMGMT_SAE
;
590 case 10: /* undefined */
593 case 11: /* 802.1x Suite-B */
594 case 12: /* 802.1x Suite-B-192 */
595 case 13: /* FT/802.1x SHA-384 */
597 c
->auth_suites
|= IWINFO_KMGMT_8021x
;
600 case 14: /* FILS SHA-256 */
601 case 15: /* FILS SHA-384 */
602 case 16: /* FT/FILS SHA-256 */
603 case 17: /* FT/FILS SHA-384 */
608 c
->auth_suites
|= IWINFO_KMGMT_OWE
;
614 data
+= 2 + (count
* 4);
615 len
-= 2 + (count
* 4);
618 struct uci_section
*iwinfo_uci_get_radio(const char *name
, const char *type
)
620 struct uci_ptr ptr
= {
621 .package
= "wireless",
623 .flags
= (name
&& *name
== '@') ? UCI_LOOKUP_EXTENDED
: 0,
628 uci_ctx
= uci_alloc_context();
633 if (uci_lookup_ptr(uci_ctx
, &ptr
, NULL
, true))
636 if (!ptr
.s
|| strcmp(ptr
.s
->type
, "wifi-device") != 0)
639 opt
= uci_lookup_option_string(uci_ctx
, ptr
.s
, "type");
640 if (!opt
|| strcmp(opt
, type
) != 0)
646 void iwinfo_uci_free(void)
651 uci_free_context(uci_ctx
);
656 struct iwinfo_ubus_query_state
{
663 static void iwinfo_ubus_query_cb(struct ubus_request
*req
, int type
,
664 struct blob_attr
*msg
)
666 struct iwinfo_ubus_query_state
*st
= req
->priv
;
668 struct blobmsg_policy pol1
[2] = {
669 { "ifname", BLOBMSG_TYPE_STRING
},
670 { "config", BLOBMSG_TYPE_TABLE
}
673 struct blobmsg_policy pol2
= { st
->field
, BLOBMSG_TYPE_STRING
};
674 struct blob_attr
*cur
, *cur2
, *cur3
, *cfg
[2], *res
;
677 blobmsg_for_each_attr(cur
, msg
, rem
) {
678 if (blobmsg_type(cur
) != BLOBMSG_TYPE_TABLE
)
681 blobmsg_for_each_attr(cur2
, cur
, rem2
) {
682 if (blobmsg_type(cur2
) != BLOBMSG_TYPE_ARRAY
)
685 if (strcmp(blobmsg_name(cur2
), "interfaces"))
688 blobmsg_for_each_attr(cur3
, cur2
, rem3
) {
689 blobmsg_parse(pol1
, sizeof(pol1
) / sizeof(pol1
[0]), cfg
,
690 blobmsg_data(cur3
), blobmsg_len(cur3
));
692 if (!cfg
[0] || !cfg
[1] ||
693 strcmp(blobmsg_get_string(cfg
[0]), st
->ifname
))
696 blobmsg_parse(&pol2
, 1, &res
,
697 blobmsg_data(cfg
[1]), blobmsg_len(cfg
[1]));
702 strncpy(st
->buf
, blobmsg_get_string(res
), st
->len
);
709 int iwinfo_ubus_query(const char *ifname
, const char *field
,
710 char *buf
, size_t len
)
712 struct iwinfo_ubus_query_state st
= {
719 struct ubus_context
*ctx
= NULL
;
720 struct blob_buf b
= { };
724 blob_buf_init(&b
, 0);
726 ctx
= ubus_connect(NULL
);
731 if (ubus_lookup_id(ctx
, "network.wireless", &id
))
734 if (ubus_invoke(ctx
, id
, "status", b
.head
, iwinfo_ubus_query_cb
, &st
, 250))