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 int n_devs
= 0;
39 static struct nl_sock
*handle
= NULL
;
40 static struct nl_cache
*cache
= NULL
;
41 static struct genl_family
*family
= NULL
;
42 static struct nlattr
*tb
[WPROBE_ATTR_LAST
+1];
43 static struct nla_policy attribute_policy
[WPROBE_ATTR_LAST
+1] = {
44 [WPROBE_ATTR_ID
] = { .type
= NLA_U32
},
45 [WPROBE_ATTR_MAC
] = { .type
= NLA_UNSPEC
, .minlen
= 6, .maxlen
= 6 },
46 [WPROBE_ATTR_NAME
] = { .type
= NLA_STRING
},
47 [WPROBE_ATTR_FLAGS
] = { .type
= NLA_U32
},
48 [WPROBE_ATTR_TYPE
] = { .type
= NLA_U8
},
49 [WPROBE_VAL_S8
] = { .type
= NLA_U8
},
50 [WPROBE_VAL_S16
] = { .type
= NLA_U16
},
51 [WPROBE_VAL_S32
] = { .type
= NLA_U32
},
52 [WPROBE_VAL_S64
] = { .type
= NLA_U64
},
53 [WPROBE_VAL_U8
] = { .type
= NLA_U8
},
54 [WPROBE_VAL_U16
] = { .type
= NLA_U16
},
55 [WPROBE_VAL_U32
] = { .type
= NLA_U32
},
56 [WPROBE_VAL_U64
] = { .type
= NLA_U64
},
57 [WPROBE_VAL_SUM
] = { .type
= NLA_U64
},
58 [WPROBE_VAL_SUM_SQ
] = { .type
= NLA_U64
},
59 [WPROBE_VAL_SAMPLES
] = { .type
= NLA_U32
},
63 error_handler(struct sockaddr_nl
*nla
, struct nlmsgerr
*err
, void *arg
)
71 finish_handler(struct nl_msg
*msg
, void *arg
)
79 ack_handler(struct nl_msg
*msg
, void *arg
)
90 /* should not happen */
100 nl_socket_free(handle
);
113 handle
= nl_socket_alloc();
115 DPRINTF("Failed to create handle\n");
119 if (genl_connect(handle
)) {
120 DPRINTF("Failed to connect to generic netlink\n");
124 ret
= genl_ctrl_alloc_cache(handle
, &cache
);
126 DPRINTF("Failed to allocate netlink cache\n");
130 family
= genl_ctrl_search_by_name(cache
, "wprobe");
132 DPRINTF("wprobe API not present\n");
143 static struct nl_msg
*
144 wprobe_new_msg(const char *ifname
, int cmd
, bool dump
)
156 genlmsg_put(msg
, 0, 0, genl_family_get_id(family
),
159 NLA_PUT_STRING(msg
, WPROBE_ATTR_INTERFACE
, ifname
);
165 wprobe_send_msg(struct nl_msg
*msg
, void *callback
, void *arg
)
170 cb
= nl_cb_alloc(NL_CB_DEFAULT
);
175 nl_cb_set(cb
, NL_CB_VALID
, NL_CB_CUSTOM
, callback
, arg
);
177 err
= nl_send_auto_complete(handle
, msg
);
183 nl_cb_err(cb
, NL_CB_CUSTOM
, error_handler
, &err
);
184 nl_cb_set(cb
, NL_CB_FINISH
, NL_CB_CUSTOM
, finish_handler
, &err
);
185 nl_cb_set(cb
, NL_CB_ACK
, NL_CB_CUSTOM
, ack_handler
, &err
);
188 nl_recvmsgs(handle
, cb
);
197 struct wprobe_attr_cb
{
198 struct list_head
*list
;
203 save_attribute_handler(struct nl_msg
*msg
, void *arg
)
205 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
206 const char *name
= "N/A";
207 struct wprobe_attribute
*attr
;
209 struct wprobe_attr_cb
*cb
= arg
;
211 nla_parse(tb
, WPROBE_ATTR_LAST
, genlmsg_attrdata(gnlh
, 0),
212 genlmsg_attrlen(gnlh
, 0), attribute_policy
);
214 if (tb
[WPROBE_ATTR_NAME
])
215 name
= nla_data(tb
[WPROBE_ATTR_NAME
]);
217 attr
= malloc(sizeof(struct wprobe_attribute
) + strlen(name
) + 1);
221 memset(attr
, 0, sizeof(struct wprobe_attribute
));
223 if (tb
[WPROBE_ATTR_ID
])
224 attr
->id
= nla_get_u32(tb
[WPROBE_ATTR_ID
]);
226 if (tb
[WPROBE_ATTR_MAC
] && cb
->addr
)
227 memcpy(cb
->addr
, nla_data(tb
[WPROBE_ATTR_MAC
]), 6);
229 if (tb
[WPROBE_ATTR_FLAGS
])
230 attr
->flags
= nla_get_u32(tb
[WPROBE_ATTR_FLAGS
]);
232 if (tb
[WPROBE_ATTR_TYPE
])
233 type
= nla_get_u8(tb
[WPROBE_ATTR_TYPE
]);
235 if ((type
< WPROBE_VAL_STRING
) ||
236 (type
> WPROBE_VAL_U64
))
240 strcpy(attr
->name
, name
);
241 INIT_LIST_HEAD(&attr
->list
);
242 list_add(&attr
->list
, cb
->list
);
248 dump_attributes(const char *ifname
, bool link
, struct list_head
*list
, char *addr
)
251 struct wprobe_attr_cb cb
;
255 msg
= wprobe_new_msg(ifname
, WPROBE_CMD_GET_LIST
, true);
260 NLA_PUT(msg
, WPROBE_ATTR_MAC
, 6, "\x00\x00\x00\x00\x00\x00");
262 return wprobe_send_msg(msg
, save_attribute_handler
, &cb
);
269 struct wprobe_iface
*
270 wprobe_get_dev(const char *ifname
)
272 struct wprobe_iface
*dev
;
274 if (wprobe_init() != 0)
277 dev
= malloc(sizeof(struct wprobe_iface
));
281 memset(dev
, 0, sizeof(struct wprobe_iface
));
282 dev
->ifname
= strdup(ifname
);
292 INIT_LIST_HEAD(&dev
->global_attr
);
293 INIT_LIST_HEAD(&dev
->link_attr
);
294 INIT_LIST_HEAD(&dev
->links
);
296 dump_attributes(ifname
, false, &dev
->global_attr
, NULL
);
297 dump_attributes(ifname
, true, &dev
->link_attr
, NULL
);
307 free_attr_list(struct list_head
*list
)
309 struct wprobe_attribute
*attr
, *tmp
;
311 list_for_each_entry_safe(attr
, tmp
, list
, list
) {
312 list_del(&attr
->list
);
318 wprobe_free_dev(struct wprobe_iface
*dev
)
321 free_attr_list(&dev
->global_attr
);
322 free_attr_list(&dev
->link_attr
);
323 free((void *)dev
->ifname
);
327 static struct wprobe_link
*
328 get_link(struct list_head
*list
, const char *addr
)
330 struct wprobe_link
*l
;
332 list_for_each_entry(l
, list
, list
) {
333 if (!memcmp(l
->addr
, addr
, 6)) {
334 list_del_init(&l
->list
);
339 /* no previous link found, allocate a new one */
340 l
= malloc(sizeof(struct wprobe_link
));
344 memset(l
, 0, sizeof(struct wprobe_link
));
345 memcpy(l
->addr
, addr
, sizeof(l
->addr
));
346 INIT_LIST_HEAD(&l
->list
);
352 struct wprobe_save_cb
{
353 struct list_head
*list
;
354 struct list_head old_list
;
358 save_link_handler(struct nl_msg
*msg
, void *arg
)
360 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
361 struct wprobe_link
*link
;
362 struct wprobe_save_cb
*cb
= arg
;
365 nla_parse(tb
, WPROBE_ATTR_LAST
, genlmsg_attrdata(gnlh
, 0),
366 genlmsg_attrlen(gnlh
, 0), attribute_policy
);
368 if (!tb
[WPROBE_ATTR_MAC
] || (nla_len(tb
[WPROBE_ATTR_MAC
]) != 6))
371 addr
= nla_data(tb
[WPROBE_ATTR_MAC
]);
372 link
= get_link(&cb
->old_list
, addr
);
376 if (tb
[WPROBE_ATTR_FLAGS
])
377 link
->flags
= nla_get_u32(tb
[WPROBE_ATTR_FLAGS
]);
379 list_add_tail(&link
->list
, cb
->list
);
385 wprobe_update_links(struct wprobe_iface
*dev
)
387 struct wprobe_link
*l
, *tmp
;
389 struct wprobe_save_cb cb
;
392 INIT_LIST_HEAD(&cb
.old_list
);
393 list_splice_init(&dev
->links
, &cb
.old_list
);
394 cb
.list
= &dev
->links
;
396 msg
= wprobe_new_msg(dev
->ifname
, WPROBE_CMD_GET_LINKS
, true);
400 err
= wprobe_send_msg(msg
, save_link_handler
, &cb
);
404 list_for_each_entry_safe(l
, tmp
, &cb
.old_list
, list
) {
413 wprobe_apply_config(struct wprobe_iface
*dev
)
417 msg
= wprobe_new_msg(dev
->ifname
, WPROBE_CMD_CONFIG
, false);
421 if (dev
->interval
>= 0)
422 NLA_PUT_MSECS(msg
, WPROBE_ATTR_INTERVAL
, dev
->interval
);
424 wprobe_send_msg(msg
, NULL
, NULL
);
433 wprobe_measure(struct wprobe_iface
*dev
)
437 msg
= wprobe_new_msg(dev
->ifname
, WPROBE_CMD_MEASURE
, false);
441 wprobe_send_msg(msg
, NULL
, NULL
);
445 struct wprobe_request_cb
{
446 struct list_head
*list
;
447 struct list_head old_list
;
452 save_attrdata_handler(struct nl_msg
*msg
, void *arg
)
454 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
455 struct wprobe_request_cb
*cb
= arg
;
456 struct wprobe_attribute
*attr
;
459 nla_parse(tb
, WPROBE_ATTR_LAST
, genlmsg_attrdata(gnlh
, 0),
460 genlmsg_attrlen(gnlh
, 0), attribute_policy
);
462 if (!tb
[WPROBE_ATTR_ID
])
465 if (!tb
[WPROBE_ATTR_TYPE
])
468 id
= nla_get_u32(tb
[WPROBE_ATTR_ID
]);
469 list_for_each_entry(attr
, &cb
->old_list
, list
) {
477 list_del_init(&attr
->list
);
479 type
= nla_get_u8(tb
[WPROBE_ATTR_TYPE
]);
480 if (type
!= attr
->type
) {
481 DPRINTF("WARNING: type mismatch for %s attribute '%s' (%d != %d)\n",
482 (cb
->addr
? "link" : "global"),
488 if ((type
< WPROBE_VAL_STRING
) ||
489 (type
> WPROBE_VAL_U64
))
492 memset(&attr
->val
, 0, sizeof(attr
->val
));
494 #define HANDLE_INT_TYPE(_idx, _type) \
495 case WPROBE_VAL_S##_type: \
496 case WPROBE_VAL_U##_type: \
497 attr->val.U##_type = nla_get_u##_type(tb[_idx]); \
501 HANDLE_INT_TYPE(type
, 8);
502 HANDLE_INT_TYPE(type
, 16);
503 HANDLE_INT_TYPE(type
, 32);
504 HANDLE_INT_TYPE(type
, 64);
505 case WPROBE_VAL_STRING
:
511 if (attr
->flags
& WPROBE_F_KEEPSTAT
) {
512 if (tb
[WPROBE_VAL_SUM
])
513 attr
->val
.s
= nla_get_u64(tb
[WPROBE_VAL_SUM
]);
515 if (tb
[WPROBE_VAL_SUM_SQ
])
516 attr
->val
.ss
= nla_get_u64(tb
[WPROBE_VAL_SUM_SQ
]);
518 if (tb
[WPROBE_VAL_SAMPLES
])
519 attr
->val
.n
= nla_get_u32(tb
[WPROBE_VAL_SAMPLES
]);
521 if (attr
->val
.n
> 0) {
522 float avg
= ((float) attr
->val
.s
) / attr
->val
.n
;
523 float stdev
= sqrt((((float) attr
->val
.ss
) / attr
->val
.n
) - (avg
* avg
));
529 attr
->val
.stdev
= stdev
;
534 list_add_tail(&attr
->list
, cb
->list
);
540 wprobe_request_data(struct wprobe_iface
*dev
, const unsigned char *addr
)
542 struct wprobe_request_cb cb
;
543 struct list_head
*attrs
;
547 msg
= wprobe_new_msg(dev
->ifname
, WPROBE_CMD_GET_INFO
, true);
552 attrs
= &dev
->link_attr
;
553 NLA_PUT(msg
, WPROBE_ATTR_MAC
, 6, addr
);
555 attrs
= &dev
->global_attr
;
558 INIT_LIST_HEAD(&cb
.old_list
);
559 list_splice_init(attrs
, &cb
.old_list
);
562 err
= wprobe_send_msg(msg
, save_attrdata_handler
, &cb
);
563 list_splice(&cb
.old_list
, attrs
->prev
);