2 * wprobe.c: Wireless probe user space library
3 * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program 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. See the
13 * GNU General Public License for more details.
21 #include <sys/types.h>
22 #include <sys/socket.h>
24 #include <linux/wprobe.h>
25 #include <netlink/netlink.h>
26 #include <netlink/genl/genl.h>
27 #include <netlink/genl/ctrl.h>
28 #include <netlink/genl/family.h>
33 #define DPRINTF(fmt, ...) fprintf(stderr, "%s(%d): " fmt, __func__, __LINE__, ##__VA_ARGS__)
35 #define DPRINTF(fmt, ...) do {} while (0)
38 static struct nl_handle
*handle
= NULL
;
39 static struct nl_cache
*cache
= NULL
;
40 static struct genl_family
*family
= NULL
;
41 static struct nlattr
*tb
[WPROBE_ATTR_LAST
+1];
42 static struct nla_policy attribute_policy
[WPROBE_ATTR_LAST
+1] = {
43 [WPROBE_ATTR_ID
] = { .type
= NLA_U32
},
44 [WPROBE_ATTR_MAC
] = { .type
= NLA_UNSPEC
, .minlen
= 6, .maxlen
= 6 },
45 [WPROBE_ATTR_NAME
] = { .type
= NLA_STRING
},
46 [WPROBE_ATTR_FLAGS
] = { .type
= NLA_U32
},
47 [WPROBE_ATTR_TYPE
] = { .type
= NLA_U8
},
48 [WPROBE_VAL_S8
] = { .type
= NLA_U8
},
49 [WPROBE_VAL_S16
] = { .type
= NLA_U16
},
50 [WPROBE_VAL_S32
] = { .type
= NLA_U32
},
51 [WPROBE_VAL_S64
] = { .type
= NLA_U64
},
52 [WPROBE_VAL_U8
] = { .type
= NLA_U8
},
53 [WPROBE_VAL_U16
] = { .type
= NLA_U16
},
54 [WPROBE_VAL_U32
] = { .type
= NLA_U32
},
55 [WPROBE_VAL_U64
] = { .type
= NLA_U64
},
56 [WPROBE_VAL_SUM
] = { .type
= NLA_U64
},
57 [WPROBE_VAL_SUM_SQ
] = { .type
= NLA_U64
},
58 [WPROBE_VAL_SAMPLES
] = { .type
= NLA_U32
},
62 error_handler(struct sockaddr_nl
*nla
, struct nlmsgerr
*err
, void *arg
)
70 finish_handler(struct nl_msg
*msg
, void *arg
)
78 ack_handler(struct nl_msg
*msg
, void *arg
)
92 nl_handle_destroy(handle
);
100 handle
= nl_handle_alloc();
102 DPRINTF("Failed to create handle\n");
106 if (genl_connect(handle
)) {
107 DPRINTF("Failed to connect to generic netlink\n");
111 cache
= genl_ctrl_alloc_cache(handle
);
113 DPRINTF("Failed to allocate netlink cache\n");
117 family
= genl_ctrl_search_by_name(cache
, "wprobe");
119 DPRINTF("wprobe API not present\n");
130 static struct nl_msg
*
131 wprobe_new_msg(const char *ifname
, int cmd
, bool dump
)
143 genlmsg_put(msg
, 0, 0, genl_family_get_id(family
),
146 NLA_PUT_STRING(msg
, WPROBE_ATTR_INTERFACE
, ifname
);
152 wprobe_send_msg(struct nl_msg
*msg
, void *callback
, void *arg
)
157 cb
= nl_cb_alloc(NL_CB_DEFAULT
);
162 nl_cb_set(cb
, NL_CB_VALID
, NL_CB_CUSTOM
, callback
, arg
);
164 err
= nl_send_auto_complete(handle
, msg
);
170 nl_cb_err(cb
, NL_CB_CUSTOM
, error_handler
, &err
);
171 nl_cb_set(cb
, NL_CB_FINISH
, NL_CB_CUSTOM
, finish_handler
, &err
);
172 nl_cb_set(cb
, NL_CB_ACK
, NL_CB_CUSTOM
, ack_handler
, &err
);
175 nl_recvmsgs(handle
, cb
);
184 struct wprobe_attr_cb
{
185 struct list_head
*list
;
190 save_attribute_handler(struct nl_msg
*msg
, void *arg
)
192 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
193 const char *name
= "N/A";
194 struct wprobe_attribute
*attr
;
196 struct wprobe_attr_cb
*cb
= arg
;
198 nla_parse(tb
, WPROBE_ATTR_LAST
, genlmsg_attrdata(gnlh
, 0),
199 genlmsg_attrlen(gnlh
, 0), attribute_policy
);
201 if (tb
[WPROBE_ATTR_NAME
])
202 name
= nla_data(tb
[WPROBE_ATTR_NAME
]);
204 attr
= malloc(sizeof(struct wprobe_attribute
) + strlen(name
) + 1);
208 memset(attr
, 0, sizeof(struct wprobe_attribute
));
210 if (tb
[WPROBE_ATTR_ID
])
211 attr
->id
= nla_get_u32(tb
[WPROBE_ATTR_ID
]);
213 if (tb
[WPROBE_ATTR_MAC
] && cb
->addr
)
214 memcpy(cb
->addr
, nla_data(tb
[WPROBE_ATTR_MAC
]), 6);
216 if (tb
[WPROBE_ATTR_FLAGS
])
217 attr
->flags
= nla_get_u32(tb
[WPROBE_ATTR_FLAGS
]);
219 if (tb
[WPROBE_ATTR_TYPE
])
220 type
= nla_get_u8(tb
[WPROBE_ATTR_TYPE
]);
222 if ((type
< WPROBE_VAL_STRING
) ||
223 (type
> WPROBE_VAL_U64
))
227 strcpy(attr
->name
, name
);
228 INIT_LIST_HEAD(&attr
->list
);
229 list_add(&attr
->list
, cb
->list
);
235 wprobe_dump_attributes(const char *ifname
, bool link
, struct list_head
*list
, char *addr
)
238 struct wprobe_attr_cb cb
;
242 msg
= wprobe_new_msg(ifname
, WPROBE_CMD_GET_LIST
, true);
247 NLA_PUT(msg
, WPROBE_ATTR_MAC
, 6, "\x00\x00\x00\x00\x00\x00");
249 return wprobe_send_msg(msg
, save_attribute_handler
, &cb
);
256 static struct wprobe_link
*
257 get_link(struct list_head
*list
, const char *addr
)
259 struct wprobe_link
*l
;
261 list_for_each_entry(l
, list
, list
) {
262 if (!memcmp(l
->addr
, addr
, 6)) {
263 list_del_init(&l
->list
);
268 /* no previous link found, allocate a new one */
269 l
= malloc(sizeof(struct wprobe_link
));
273 memset(l
, 0, sizeof(struct wprobe_link
));
274 memcpy(l
->addr
, addr
, sizeof(l
->addr
));
275 INIT_LIST_HEAD(&l
->list
);
281 struct wprobe_save_cb
{
282 struct list_head
*list
;
283 struct list_head old_list
;
287 save_link_handler(struct nl_msg
*msg
, void *arg
)
289 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
290 struct wprobe_link
*link
;
291 struct wprobe_save_cb
*cb
= arg
;
294 nla_parse(tb
, WPROBE_ATTR_LAST
, genlmsg_attrdata(gnlh
, 0),
295 genlmsg_attrlen(gnlh
, 0), attribute_policy
);
297 if (!tb
[WPROBE_ATTR_MAC
] || (nla_len(tb
[WPROBE_ATTR_MAC
]) != 6))
300 addr
= nla_data(tb
[WPROBE_ATTR_MAC
]);
301 link
= get_link(&cb
->old_list
, addr
);
305 if (tb
[WPROBE_ATTR_FLAGS
])
306 link
->flags
= nla_get_u32(tb
[WPROBE_ATTR_FLAGS
]);
308 list_add_tail(&link
->list
, cb
->list
);
314 wprobe_update_links(const char *ifname
, struct list_head
*list
)
316 struct wprobe_link
*l
, *tmp
;
318 struct wprobe_save_cb cb
;
321 INIT_LIST_HEAD(&cb
.old_list
);
322 list_splice_init(list
, &cb
.old_list
);
325 msg
= wprobe_new_msg(ifname
, WPROBE_CMD_GET_LINKS
, true);
329 err
= wprobe_send_msg(msg
, save_link_handler
, &cb
);
333 list_for_each_entry_safe(l
, tmp
, &cb
.old_list
, list
) {
342 wprobe_measure(const char *ifname
)
346 msg
= wprobe_new_msg(ifname
, WPROBE_CMD_MEASURE
, false);
350 wprobe_send_msg(msg
, NULL
, NULL
);
353 struct wprobe_request_cb
{
354 struct list_head
*list
;
355 struct list_head old_list
;
360 save_attrdata_handler(struct nl_msg
*msg
, void *arg
)
362 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
363 struct wprobe_request_cb
*cb
= arg
;
364 struct wprobe_attribute
*attr
;
367 nla_parse(tb
, WPROBE_ATTR_LAST
, genlmsg_attrdata(gnlh
, 0),
368 genlmsg_attrlen(gnlh
, 0), attribute_policy
);
370 if (!tb
[WPROBE_ATTR_ID
])
373 if (!tb
[WPROBE_ATTR_TYPE
])
376 id
= nla_get_u32(tb
[WPROBE_ATTR_ID
]);
377 list_for_each_entry(attr
, &cb
->old_list
, list
) {
385 list_del_init(&attr
->list
);
387 type
= nla_get_u8(tb
[WPROBE_ATTR_TYPE
]);
388 if (type
!= attr
->type
) {
389 DPRINTF("WARNING: type mismatch for %s attribute '%s' (%d != %d)\n",
390 (cb
->addr
? "link" : "global"),
396 if ((type
< WPROBE_VAL_STRING
) ||
397 (type
> WPROBE_VAL_U64
))
400 memset(&attr
->val
, 0, sizeof(attr
->val
));
402 #define HANDLE_INT_TYPE(_idx, _type) \
403 case WPROBE_VAL_S##_type: \
404 case WPROBE_VAL_U##_type: \
405 attr->val.U##_type = nla_get_u##_type(tb[_idx]); \
409 HANDLE_INT_TYPE(type
, 8);
410 HANDLE_INT_TYPE(type
, 16);
411 HANDLE_INT_TYPE(type
, 32);
412 HANDLE_INT_TYPE(type
, 64);
413 case WPROBE_VAL_STRING
:
419 if (attr
->flags
& WPROBE_F_KEEPSTAT
) {
420 if (tb
[WPROBE_VAL_SUM
])
421 attr
->val
.s
= nla_get_u64(tb
[WPROBE_VAL_SUM
]);
423 if (tb
[WPROBE_VAL_SUM_SQ
])
424 attr
->val
.ss
= nla_get_u64(tb
[WPROBE_VAL_SUM_SQ
]);
426 if (tb
[WPROBE_VAL_SAMPLES
])
427 attr
->val
.n
= nla_get_u32(tb
[WPROBE_VAL_SAMPLES
]);
429 if (attr
->val
.n
> 0) {
430 float avg
= ((float) attr
->val
.s
) / attr
->val
.n
;
431 float stdev
= sqrt((((float) attr
->val
.ss
) / attr
->val
.n
) - (avg
* avg
));
433 attr
->val
.stdev
= stdev
;
438 list_add_tail(&attr
->list
, cb
->list
);
444 wprobe_request_data(const char *ifname
, struct list_head
*attrs
, const unsigned char *addr
, int scale
)
446 struct wprobe_request_cb cb
;
450 msg
= wprobe_new_msg(ifname
, WPROBE_CMD_GET_INFO
, true);
455 NLA_PUT_U32(msg
, WPROBE_ATTR_FLAGS
, WPROBE_F_RESET
);
457 NLA_PUT_U32(msg
, WPROBE_ATTR_SCALE
, scale
);
460 NLA_PUT(msg
, WPROBE_ATTR_MAC
, 6, addr
);
463 INIT_LIST_HEAD(&cb
.old_list
);
464 list_splice_init(attrs
, &cb
.old_list
);
467 err
= wprobe_send_msg(msg
, save_attrdata_handler
, &cb
);
468 list_splice(&cb
.old_list
, attrs
->prev
);