]> git.ipfire.org Git - thirdparty/iw.git/blame - station.c
add back the ability to add a sibling interface
[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
39static 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
46static 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);
fbb181fa 83 printf("Station %s (on %s)", mac_addr, dev);
3d1e8704
LCC
84
85 if (sinfo[NL80211_STA_INFO_INACTIVE_TIME])
fbb181fa 86 printf("\n\tinactive time:\t%d ms",
3d1e8704
LCC
87 nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME]));
88 if (sinfo[NL80211_STA_INFO_RX_BYTES])
fbb181fa 89 printf("\n\trx bytes:\t%d",
3d1e8704
LCC
90 nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]));
91 if (sinfo[NL80211_STA_INFO_TX_BYTES])
fbb181fa 92 printf("\n\ttx bytes:\t%d",
3d1e8704
LCC
93 nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]));
94 if (sinfo[NL80211_STA_INFO_LLID])
fbb181fa 95 printf("\n\tmesh llid:\t%d",
3d1e8704
LCC
96 nla_get_u16(sinfo[NL80211_STA_INFO_LLID]));
97 if (sinfo[NL80211_STA_INFO_PLID])
fbb181fa 98 printf("\n\tmesh plid:\t%d",
3d1e8704
LCC
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 }
fbb181fa 127 printf("\n\tmesh plink:\t%s", state_name);
3d1e8704
LCC
128 }
129
130 printf("\n");
131 return NL_SKIP;
132}
133
134static int handle_station_get(struct nl80211_state *state,
b1ca19a8
JB
135 struct nl_msg *msg,
136 int argc, char **argv)
3d1e8704 137{
3d1e8704 138 struct nl_cb *cb = NULL;
b1ca19a8 139 int ret = 1;
3d1e8704
LCC
140 int err;
141 int finished = 0;
142 unsigned char mac_addr[ETH_ALEN];
143
b1ca19a8 144 if (argc < 1)
3d1e8704 145 return -1;
3d1e8704
LCC
146
147 if (mac_addr_a2n(mac_addr, argv[0])) {
148 fprintf(stderr, "invalid mac address\n");
b1ca19a8 149 return 1;
3d1e8704
LCC
150 }
151
152 argc--;
153 argv++;
154
b1ca19a8 155 if (argc)
3d1e8704 156 return -1;
3d1e8704
LCC
157
158 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
3d1e8704
LCC
159
160 cb = nl_cb_alloc(NL_CB_CUSTOM);
161 if (!cb)
162 goto out;
163
164 if (nl_send_auto_complete(state->nl_handle, msg) < 0)
165 goto out;
166
167 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL);
168 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
169 nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL);
170
171 err = nl_recvmsgs(state->nl_handle, cb);
172
173 if (!finished)
174 err = nl_wait_for_ack(state->nl_handle);
175
176 if (err < 0)
177 goto out;
178
179 ret = 0;
180
181 out:
182 nl_cb_put(cb);
183 nla_put_failure:
3d1e8704
LCC
184 return ret;
185}
b1ca19a8
JB
186COMMAND(station, get, "<MAC address>",
187 NL80211_CMD_GET_STATION, 0, CIB_NETDEV, handle_station_get);
188COMMAND(station, del, "<MAC address>",
189 NL80211_CMD_DEL_STATION, 0, CIB_NETDEV, handle_station_get);
3d1e8704
LCC
190
191static int handle_station_set(struct nl80211_state *state,
b1ca19a8
JB
192 struct nl_msg *msg,
193 int argc, char **argv)
3d1e8704 194{
3d1e8704 195 struct nl_cb *cb = NULL;
b1ca19a8 196 int ret = 1;
3d1e8704
LCC
197 int err;
198 int finished = 0;
199 unsigned char plink_action;
200 unsigned char mac_addr[ETH_ALEN];
201
b1ca19a8 202 if (argc < 3)
3d1e8704 203 return -1;
3d1e8704
LCC
204
205 if (mac_addr_a2n(mac_addr, argv[0])) {
206 fprintf(stderr, "invalid mac address\n");
b1ca19a8 207 return 1;
3d1e8704
LCC
208 }
209 argc--;
210 argv++;
211
b1ca19a8
JB
212 if (strcmp("plink_action", argv[0]) != 0)
213 return 1;
3d1e8704
LCC
214 argc--;
215 argv++;
216
217 if (strcmp("open", argv[0]) == 0)
218 plink_action = PLINK_ACTION_OPEN;
219 else if (strcmp("block", argv[0]) == 0)
220 plink_action = PLINK_ACTION_BLOCK;
221 else {
222 fprintf(stderr, "plink action not supported\n");
b1ca19a8 223 return 1;
3d1e8704
LCC
224 }
225 argc--;
226 argv++;
227
b1ca19a8 228 if (argc)
3d1e8704 229 return -1;
3d1e8704
LCC
230
231 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
3d1e8704
LCC
232 NLA_PUT_U8(msg, NL80211_ATTR_STA_PLINK_ACTION, plink_action);
233
234 cb = nl_cb_alloc(NL_CB_CUSTOM);
235 if (!cb)
236 goto out;
237
238 if (nl_send_auto_complete(state->nl_handle, msg) < 0)
239 goto out;
240
241 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL);
242 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
243 nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL);
244
245 err = nl_recvmsgs(state->nl_handle, cb);
246
247 if (!finished)
248 err = nl_wait_for_ack(state->nl_handle);
249
250 if (err < 0)
251 goto out;
252
253 ret = 0;
254
255 out:
256 nl_cb_put(cb);
257 nla_put_failure:
3d1e8704
LCC
258 return ret;
259}
b1ca19a8
JB
260COMMAND(station, set, "<MAC address> plink_action <open|block>",
261 NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set);
262
3d1e8704 263static int handle_station_dump(struct nl80211_state *state,
b1ca19a8
JB
264 struct nl_msg *msg,
265 int argc, char **argv)
3d1e8704 266{
3d1e8704 267 struct nl_cb *cb = NULL;
b1ca19a8 268 int ret = 1;
3d1e8704
LCC
269 int err;
270 int finished = 0;
271
b1ca19a8 272 if (argc)
3d1e8704 273 return -1;
3d1e8704
LCC
274
275 cb = nl_cb_alloc(NL_CB_CUSTOM);
276 if (!cb)
277 goto out;
278
279 if (nl_send_auto_complete(state->nl_handle, msg) < 0)
280 goto out;
281
282 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL);
283 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, wait_handler, &finished);
284
285 err = nl_recvmsgs(state->nl_handle, cb);
286
287 if (err < 0)
288 goto out;
289
290 ret = 0;
291
292 out:
293 nl_cb_put(cb);
3d1e8704
LCC
294 return ret;
295}
b1ca19a8
JB
296COMMAND(station, dump, NULL,
297 NL80211_CMD_SET_STATION, NLM_F_DUMP, CIB_NETDEV, handle_station_dump);