Home | History | Annotate | Download | only in iw
      1 #include <net/if.h>
      2 #include <errno.h>
      3 #include <string.h>
      4 #include <ctype.h>
      5 #include <stdbool.h>
      6 
      7 #include <netlink/genl/genl.h>
      8 #include <netlink/genl/family.h>
      9 #include <netlink/genl/ctrl.h>
     10 #include <netlink/msg.h>
     11 #include <netlink/attr.h>
     12 
     13 #include "nl80211.h"
     14 #include "iw.h"
     15 
     16 struct link_result {
     17 	uint8_t bssid[8];
     18 	bool link_found;
     19 	bool anything_found;
     20 };
     21 
     22 static struct link_result lr = { .link_found = false };
     23 
     24 static int link_bss_handler(struct nl_msg *msg, void *arg)
     25 {
     26 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
     27 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
     28 	struct nlattr *bss[NL80211_BSS_MAX + 1];
     29 	static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
     30 		[NL80211_BSS_TSF] = { .type = NLA_U64 },
     31 		[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
     32 		[NL80211_BSS_BSSID] = { },
     33 		[NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
     34 		[NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
     35 		[NL80211_BSS_INFORMATION_ELEMENTS] = { },
     36 		[NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
     37 		[NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
     38 		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
     39 	};
     40 	struct link_result *result = arg;
     41 	char mac_addr[20], dev[20];
     42 
     43 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
     44 		  genlmsg_attrlen(gnlh, 0), NULL);
     45 
     46 	if (!tb[NL80211_ATTR_BSS]) {
     47 		fprintf(stderr, "bss info missing!\n");
     48 		return NL_SKIP;
     49 	}
     50 	if (nla_parse_nested(bss, NL80211_BSS_MAX,
     51 			     tb[NL80211_ATTR_BSS],
     52 			     bss_policy)) {
     53 		fprintf(stderr, "failed to parse nested attributes!\n");
     54 		return NL_SKIP;
     55 	}
     56 
     57 	if (!bss[NL80211_BSS_BSSID])
     58 		return NL_SKIP;
     59 
     60 	if (!bss[NL80211_BSS_STATUS])
     61 		return NL_SKIP;
     62 
     63 	mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
     64 	if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
     65 
     66 	switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
     67 	case NL80211_BSS_STATUS_ASSOCIATED:
     68 		printf("Connected to %s (on %s)\n", mac_addr, dev);
     69 		break;
     70 	case NL80211_BSS_STATUS_AUTHENTICATED:
     71 		printf("Authenticated with %s (on %s)\n", mac_addr, dev);
     72 		return NL_SKIP;
     73 	case NL80211_BSS_STATUS_IBSS_JOINED:
     74 		printf("Joined IBSS %s (on %s)\n", mac_addr, dev);
     75 		break;
     76 	default:
     77 		return NL_SKIP;
     78 	}
     79 
     80 	result->anything_found = true;
     81 
     82 	if (bss[NL80211_BSS_INFORMATION_ELEMENTS])
     83 		print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
     84 			  nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
     85 			  false, PRINT_LINK);
     86 
     87 	if (bss[NL80211_BSS_FREQUENCY])
     88 		printf("\tfreq: %d\n",
     89 			nla_get_u32(bss[NL80211_BSS_FREQUENCY]));
     90 
     91 	if (nla_get_u32(bss[NL80211_BSS_STATUS]) != NL80211_BSS_STATUS_ASSOCIATED)
     92 		return NL_SKIP;
     93 
     94 	/* only in the assoc case do we want more info from station get */
     95 	result->link_found = true;
     96 	memcpy(result->bssid, nla_data(bss[NL80211_BSS_BSSID]), 6);
     97 	return NL_SKIP;
     98 }
     99 
    100 static int handle_scan_for_link(struct nl80211_state *state,
    101 				struct nl_cb *cb,
    102 				struct nl_msg *msg,
    103 				int argc, char **argv,
    104 				enum id_input id)
    105 {
    106 	if (argc > 0)
    107 		return 1;
    108 
    109 	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, link_bss_handler, &lr);
    110 	return 0;
    111 }
    112 
    113 static int print_link_sta(struct nl_msg *msg, void *arg)
    114 {
    115 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
    116 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
    117 	struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
    118 	struct nlattr *binfo[NL80211_STA_BSS_PARAM_MAX + 1];
    119 	static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
    120 		[NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
    121 		[NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
    122 		[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
    123 		[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
    124 		[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
    125 		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
    126 		[NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
    127 		[NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
    128 		[NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
    129 		[NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
    130 	};
    131 	static struct nla_policy bss_policy[NL80211_STA_BSS_PARAM_MAX + 1] = {
    132 		[NL80211_STA_BSS_PARAM_CTS_PROT] = { .type = NLA_FLAG },
    133 		[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE] = { .type = NLA_FLAG },
    134 		[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME] = { .type = NLA_FLAG },
    135 		[NL80211_STA_BSS_PARAM_DTIM_PERIOD] = { .type = NLA_U8 },
    136 		[NL80211_STA_BSS_PARAM_BEACON_INTERVAL] = { .type = NLA_U16 },
    137 	};
    138 
    139 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
    140 		  genlmsg_attrlen(gnlh, 0), NULL);
    141 
    142 	if (!tb[NL80211_ATTR_STA_INFO]) {
    143 		fprintf(stderr, "sta stats missing!\n");
    144 		return NL_SKIP;
    145 	}
    146 	if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
    147 			     tb[NL80211_ATTR_STA_INFO],
    148 			     stats_policy)) {
    149 		fprintf(stderr, "failed to parse nested attributes!\n");
    150 		return NL_SKIP;
    151 	}
    152 
    153 	if (sinfo[NL80211_STA_INFO_RX_BYTES] && sinfo[NL80211_STA_INFO_RX_PACKETS])
    154 		printf("\tRX: %u bytes (%u packets)\n",
    155 			nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]),
    156 			nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]));
    157 	if (sinfo[NL80211_STA_INFO_TX_BYTES] && sinfo[NL80211_STA_INFO_TX_PACKETS])
    158 		printf("\tTX: %u bytes (%u packets)\n",
    159 			nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]),
    160 			nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]));
    161 	if (sinfo[NL80211_STA_INFO_SIGNAL])
    162 		printf("\tsignal: %d dBm\n",
    163 			(int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]));
    164 
    165 	if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
    166 		char buf[100];
    167 
    168 		parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf));
    169 		printf("\ttx bitrate: %s\n", buf);
    170 	}
    171 
    172 	if (sinfo[NL80211_STA_INFO_BSS_PARAM]) {
    173 		if (nla_parse_nested(binfo, NL80211_STA_BSS_PARAM_MAX,
    174 				     sinfo[NL80211_STA_INFO_BSS_PARAM],
    175 				     bss_policy)) {
    176 			fprintf(stderr, "failed to parse nested bss parameters!\n");
    177 		} else {
    178 			char *delim = "";
    179 			printf("\n\tbss flags:\t");
    180 			if (binfo[NL80211_STA_BSS_PARAM_CTS_PROT]) {
    181 				printf("CTS-protection");
    182 				delim = " ";
    183 			}
    184 			if (binfo[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE]) {
    185 				printf("%sshort-preamble", delim);
    186 				delim = " ";
    187 			}
    188 			if (binfo[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME])
    189 				printf("%sshort-slot-time", delim);
    190 			printf("\n\tdtim period:\t%d",
    191 			       nla_get_u8(binfo[NL80211_STA_BSS_PARAM_DTIM_PERIOD]));
    192 			printf("\n\tbeacon int:\t%d",
    193 			       nla_get_u16(binfo[NL80211_STA_BSS_PARAM_BEACON_INTERVAL]));
    194 			printf("\n");
    195 		}
    196 	}
    197 
    198 	return NL_SKIP;
    199 }
    200 
    201 static int handle_link_sta(struct nl80211_state *state,
    202 			   struct nl_cb *cb,
    203 			   struct nl_msg *msg,
    204 			   int argc, char **argv,
    205 			   enum id_input id)
    206 {
    207 	unsigned char mac_addr[ETH_ALEN];
    208 
    209 	if (argc < 1)
    210 		return 1;
    211 
    212 	if (mac_addr_a2n(mac_addr, argv[0])) {
    213 		fprintf(stderr, "invalid mac address\n");
    214 		return 2;
    215 	}
    216 
    217 	argc--;
    218 	argv++;
    219 
    220 	if (argc)
    221 		return 1;
    222 
    223 	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
    224 
    225 	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_link_sta, NULL);
    226 
    227 	return 0;
    228  nla_put_failure:
    229 	return -ENOBUFS;
    230 }
    231 
    232 static int handle_link(struct nl80211_state *state, struct nl_cb *cb,
    233 		       struct nl_msg *msg, int argc, char **argv,
    234 		       enum id_input id)
    235 {
    236 	char *link_argv[] = {
    237 		NULL,
    238 		"link",
    239 		"get_bss",
    240 		NULL,
    241 	};
    242 	char *station_argv[] = {
    243 		NULL,
    244 		"link",
    245 		"get_sta",
    246 		NULL,
    247 		NULL,
    248 	};
    249 	char bssid_buf[3*6];
    250 	int err;
    251 
    252 	link_argv[0] = argv[0];
    253 	err = handle_cmd(state, id, 3, link_argv);
    254 	if (err)
    255 		return err;
    256 
    257 	if (!lr.link_found) {
    258 		if (!lr.anything_found)
    259 			printf("Not connected.\n");
    260 		return 0;
    261 	}
    262 
    263 	mac_addr_n2a(bssid_buf, lr.bssid);
    264 	bssid_buf[17] = '\0';
    265 
    266 	station_argv[0] = argv[0];
    267 	station_argv[3] = bssid_buf;
    268 	return handle_cmd(state, id, 4, station_argv);
    269 }
    270 TOPLEVEL(link, NULL, 0, 0, CIB_NETDEV, handle_link,
    271 	 "Print information about the current link, if any.");
    272 HIDDEN(link, get_sta, "", NL80211_CMD_GET_STATION, 0,
    273 	CIB_NETDEV, handle_link_sta);
    274 HIDDEN(link, get_bss, NULL, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
    275 	CIB_NETDEV, handle_scan_for_link);
    276