]> git.ipfire.org Git - thirdparty/iw.git/blame - station.c
fix up some error handling
[thirdparty/iw.git] / station.c
CommitLineData
2ef1be68
JB
1#include <linux/nl80211.h>
2#include <net/if.h>
3d1e8704 3#include <errno.h>
d5ac8ad3 4#include <string.h>
2ef1be68 5
3d1e8704
LCC
6#include <netlink/genl/genl.h>
7#include <netlink/genl/family.h>
8#include <netlink/genl/ctrl.h>
9#include <netlink/msg.h>
10#include <netlink/attr.h>
3d1e8704
LCC
11
12#include "iw.h"
13
14enum plink_state {
15 LISTEN,
16 OPN_SNT,
17 OPN_RCVD,
18 CNF_RCVD,
19 ESTAB,
20 HOLDING,
21 BLOCKED
22};
23
24enum plink_actions {
25 PLINK_ACTION_UNDEFINED,
26 PLINK_ACTION_OPEN,
27 PLINK_ACTION_BLOCK,
28};
29
30
31static int wait_handler(struct nl_msg *msg, void *arg)
32{
33 int *finished = arg;
34
35 *finished = 1;
36 return NL_STOP;
37}
38
3d1e8704
LCC
39static int print_sta_handler(struct nl_msg *msg, void *arg)
40{
41 struct nlattr *tb[NL80211_ATTR_MAX + 1];
42 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
43 struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
44 char mac_addr[20], state_name[10], dev[20];
45 static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
46 [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
47 [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
48 [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
49 [NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
50 [NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
51 [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
52 };
53
54 nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
55 genlmsg_attrlen(gnlh, 0), NULL);
56
57 /*
58 * TODO: validate the interface and mac address!
59 * Otherwise, there's a race condition as soon as
60 * the kernel starts sending station notifications.
61 */
62
63 if (!tb[NL80211_ATTR_STA_INFO]) {
64 fprintf(stderr, "sta stats missing!");
65 return NL_SKIP;
66 }
67 if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
68 tb[NL80211_ATTR_STA_INFO],
69 stats_policy)) {
70 fprintf(stderr, "failed to parse nested attributes!");
71 return NL_SKIP;
72 }
73
74 mac_addr_n2a(mac_addr, nla_data(tb[NL80211_ATTR_MAC]));
75 if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
fbb181fa 76 printf("Station %s (on %s)", mac_addr, dev);
3d1e8704
LCC
77
78 if (sinfo[NL80211_STA_INFO_INACTIVE_TIME])
fbb181fa 79 printf("\n\tinactive time:\t%d ms",
3d1e8704
LCC
80 nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME]));
81 if (sinfo[NL80211_STA_INFO_RX_BYTES])
fbb181fa 82 printf("\n\trx bytes:\t%d",
3d1e8704
LCC
83 nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]));
84 if (sinfo[NL80211_STA_INFO_TX_BYTES])
fbb181fa 85 printf("\n\ttx bytes:\t%d",
3d1e8704
LCC
86 nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]));
87 if (sinfo[NL80211_STA_INFO_LLID])
fbb181fa 88 printf("\n\tmesh llid:\t%d",
3d1e8704
LCC
89 nla_get_u16(sinfo[NL80211_STA_INFO_LLID]));
90 if (sinfo[NL80211_STA_INFO_PLID])
fbb181fa 91 printf("\n\tmesh plid:\t%d",
3d1e8704
LCC
92 nla_get_u16(sinfo[NL80211_STA_INFO_PLID]));
93 if (sinfo[NL80211_STA_INFO_PLINK_STATE]) {
94 switch (nla_get_u16(sinfo[NL80211_STA_INFO_PLINK_STATE])) {
95 case LISTEN:
96 strcpy(state_name, "LISTEN");
97 break;
98 case OPN_SNT:
99 strcpy(state_name, "OPN_SNT");
100 break;
101 case OPN_RCVD:
102 strcpy(state_name, "OPN_RCVD");
103 break;
104 case CNF_RCVD:
105 strcpy(state_name, "CNF_RCVD");
106 break;
107 case ESTAB:
108 strcpy(state_name, "ESTAB");
109 break;
110 case HOLDING:
111 strcpy(state_name, "HOLDING");
112 break;
113 case BLOCKED:
114 strcpy(state_name, "BLOCKED");
115 break;
116 default:
117 strcpy(state_name, "UNKNOWN");
118 break;
119 }
fbb181fa 120 printf("\n\tmesh plink:\t%s", state_name);
3d1e8704
LCC
121 }
122
123 printf("\n");
124 return NL_SKIP;
125}
126
127static int handle_station_get(struct nl80211_state *state,
b1ca19a8
JB
128 struct nl_msg *msg,
129 int argc, char **argv)
3d1e8704 130{
3d1e8704 131 struct nl_cb *cb = NULL;
5e75fd04 132 int err = -ENOMEM;
3d1e8704
LCC
133 int finished = 0;
134 unsigned char mac_addr[ETH_ALEN];
135
b1ca19a8 136 if (argc < 1)
5e75fd04 137 return 1;
3d1e8704
LCC
138
139 if (mac_addr_a2n(mac_addr, argv[0])) {
140 fprintf(stderr, "invalid mac address\n");
5e75fd04 141 return 2;
3d1e8704
LCC
142 }
143
144 argc--;
145 argv++;
146
b1ca19a8 147 if (argc)
5e75fd04 148 return 1;
3d1e8704
LCC
149
150 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
3d1e8704
LCC
151
152 cb = nl_cb_alloc(NL_CB_CUSTOM);
153 if (!cb)
154 goto out;
155
156 if (nl_send_auto_complete(state->nl_handle, msg) < 0)
157 goto out;
158
159 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL);
160 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
161 nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL);
162
5e75fd04
JB
163 nl_recvmsgs(state->nl_handle, cb);
164 err = 0;
3d1e8704
LCC
165
166 if (!finished)
167 err = nl_wait_for_ack(state->nl_handle);
168
3d1e8704
LCC
169 out:
170 nl_cb_put(cb);
171 nla_put_failure:
5e75fd04 172 return err;
3d1e8704 173}
b1ca19a8
JB
174COMMAND(station, get, "<MAC address>",
175 NL80211_CMD_GET_STATION, 0, CIB_NETDEV, handle_station_get);
176COMMAND(station, del, "<MAC address>",
177 NL80211_CMD_DEL_STATION, 0, CIB_NETDEV, handle_station_get);
3d1e8704
LCC
178
179static int handle_station_set(struct nl80211_state *state,
b1ca19a8
JB
180 struct nl_msg *msg,
181 int argc, char **argv)
3d1e8704 182{
3d1e8704 183 struct nl_cb *cb = NULL;
5e75fd04 184 int err = -ENOMEM;
3d1e8704
LCC
185 int finished = 0;
186 unsigned char plink_action;
187 unsigned char mac_addr[ETH_ALEN];
188
b1ca19a8 189 if (argc < 3)
5e75fd04 190 return 1;
3d1e8704
LCC
191
192 if (mac_addr_a2n(mac_addr, argv[0])) {
193 fprintf(stderr, "invalid mac address\n");
5e75fd04 194 return 2;
3d1e8704
LCC
195 }
196 argc--;
197 argv++;
198
b1ca19a8
JB
199 if (strcmp("plink_action", argv[0]) != 0)
200 return 1;
3d1e8704
LCC
201 argc--;
202 argv++;
203
204 if (strcmp("open", argv[0]) == 0)
205 plink_action = PLINK_ACTION_OPEN;
206 else if (strcmp("block", argv[0]) == 0)
207 plink_action = PLINK_ACTION_BLOCK;
208 else {
209 fprintf(stderr, "plink action not supported\n");
5e75fd04 210 return 2;
3d1e8704
LCC
211 }
212 argc--;
213 argv++;
214
b1ca19a8 215 if (argc)
5e75fd04 216 return 1;
3d1e8704
LCC
217
218 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
3d1e8704
LCC
219 NLA_PUT_U8(msg, NL80211_ATTR_STA_PLINK_ACTION, plink_action);
220
221 cb = nl_cb_alloc(NL_CB_CUSTOM);
222 if (!cb)
223 goto out;
224
5e75fd04 225 if ((err = nl_send_auto_complete(state->nl_handle, msg)) < 0)
3d1e8704
LCC
226 goto out;
227
228 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL);
229 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
230 nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL);
231
5e75fd04
JB
232 nl_recvmsgs(state->nl_handle, cb);
233 err = 0;
3d1e8704
LCC
234
235 if (!finished)
236 err = nl_wait_for_ack(state->nl_handle);
237
3d1e8704
LCC
238 out:
239 nl_cb_put(cb);
240 nla_put_failure:
5e75fd04 241 return err;
3d1e8704 242}
b1ca19a8
JB
243COMMAND(station, set, "<MAC address> plink_action <open|block>",
244 NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set);
245
3d1e8704 246static int handle_station_dump(struct nl80211_state *state,
b1ca19a8
JB
247 struct nl_msg *msg,
248 int argc, char **argv)
3d1e8704 249{
3d1e8704 250 struct nl_cb *cb = NULL;
5e75fd04 251 int err = -ENOMEM;
3d1e8704
LCC
252 int finished = 0;
253
b1ca19a8 254 if (argc)
5e75fd04 255 return 1;
3d1e8704
LCC
256
257 cb = nl_cb_alloc(NL_CB_CUSTOM);
258 if (!cb)
259 goto out;
260
5e75fd04 261 if ((err = nl_send_auto_complete(state->nl_handle, msg)) < 0)
3d1e8704
LCC
262 goto out;
263
264 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL);
e3c1355d 265 nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &finished);
3d1e8704
LCC
266 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, wait_handler, &finished);
267
5e75fd04 268 nl_recvmsgs(state->nl_handle, cb);
e3c1355d 269 err = finished;
3d1e8704 270
5e75fd04
JB
271 if (!finished)
272 err = nl_wait_for_ack(state->nl_handle);
3d1e8704
LCC
273
274 out:
275 nl_cb_put(cb);
5e75fd04 276 return err;
3d1e8704 277}
b1ca19a8
JB
278COMMAND(station, dump, NULL,
279 NL80211_CMD_SET_STATION, NLM_F_DUMP, CIB_NETDEV, handle_station_dump);