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