ddccfeb2cfeba220b7ce8e48755a038f18cc72cf
[oonf.git] / src / generic / nl80211_listener / nl80211_get_station_dump.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /**
43  * @file
44  */
45
46 /*
47  * Much of the code of this file originally came from the iw userspace
48  * command source code and was adapted for OLSRv2.
49  *
50  * Copyright (c) 2007, 2008 Johannes Berg
51  * Copyright (c) 2007    Andy Lutomirski
52  * Copyright (c) 2007    Mike Kershaw
53  * Copyright (c) 2008-2009   Luis R. Rodriguez
54  *
55  * Permission to use, copy, modify, and/or distribute this software for any
56  * purpose with or without fee is hereby granted, provided that the above
57  * copyright notice and this permission notice appear in all copies.
58  *
59  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
60  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
61  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
62  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
63  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
64  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
65  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
66  */
67
68 #define _GNU_SOURCE
69
70 /* must be first because of a problem with linux/netlink.h */
71 #include <sys/socket.h>
72
73 /* and now the rest of the includes */
74 #include <linux/genetlink.h>
75 #include <linux/netlink.h>
76 #include <linux/types.h>
77 #include <netlink/attr.h>
78 #include <netlink/genl/genl.h>
79 #include <netlink/msg.h>
80
81 #include <oonf/oonf.h>
82 #include <oonf/subsystems/oonf_clock.h>
83 #include <oonf/subsystems/os_system.h>
84
85 #include <oonf/generic/nl80211_listener/nl80211.h>
86 #include <oonf/generic/nl80211_listener/nl80211_get_station_dump.h>
87 #include <oonf/generic/nl80211_listener/nl80211_internal.h>
88 #include <oonf/generic/nl80211_listener/nl80211_listener.h>
89
90 static bool _handle_traffic(struct oonf_layer2_neigh *l2neigh, enum oonf_layer2_neighbor_index idx, uint32_t new_32bit);
91 static int64_t _get_bitrate(struct nlattr *bitrate_attr);
92
93 /**
94  * Send a netlink message to get the nl80211 station dump
95  * @param nl pointer to netlink handler
96  * @param nl_msg pointer to netlink message
97  * @param hdr pointer to generic netlink header
98  * @param interf nl80211 listener interface
99  */
100 void
101 nl80211_send_get_station_dump(
102   struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf) {
103   int if_index = nl80211_get_if_baseindex(interf);
104
105   hdr->cmd = NL80211_CMD_GET_STATION;
106   nl_msg->nlmsg_flags |= NLM_F_DUMP;
107
108   /* add interface index to the request */
109   os_system_linux_netlink_addreq(nl, nl_msg, NL80211_ATTR_IFINDEX, &if_index, sizeof(if_index));
110 }
111
112 /**
113  * Process NL80211_CMD_NEW_STATION message
114  * @param interf nl80211 listener interface
115  * @param hdr pointer to netlink message header
116  */
117 void
118 nl80211_process_get_station_dump_result(struct nl80211_if *interf, struct nlmsghdr *hdr) {
119   struct oonf_layer2_neigh *l2neigh;
120   struct netaddr l2neigh_mac;
121
122   struct nlattr *tb[NL80211_ATTR_MAX + 1];
123   struct genlmsghdr *gnlh;
124   struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
125   static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
126     [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
127     [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
128     [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
129     [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
130     [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
131     [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
132     [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
133     [NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED },
134     [NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 },
135     [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
136     [NL80211_STA_INFO_STA_FLAGS] = { .minlen = sizeof(struct nl80211_sta_flag_update) },
137   };
138 #ifdef OONF_LOG_DEBUG_INFO
139   struct netaddr_str nbuf;
140 #endif
141
142   gnlh = nlmsg_data(hdr);
143
144   nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
145
146   if (!tb[NL80211_ATTR_STA_INFO]) {
147     /* station info missing */
148     return;
149   }
150   if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, tb[NL80211_ATTR_STA_INFO], stats_policy)) {
151     /* no nested attributes */
152     return;
153   }
154
155   if (nl80211_get_if_baseindex(interf) != nla_get_u32(tb[NL80211_ATTR_IFINDEX])) {
156     /* wrong interface */
157     return;
158   }
159
160   netaddr_from_binary(&l2neigh_mac, nla_data(tb[NL80211_ATTR_MAC]), 6, AF_MAC48);
161
162   OONF_DEBUG(LOG_NL80211, "Received Station Dump for %s", netaddr_to_string(&nbuf, &l2neigh_mac));
163
164   l2neigh = oonf_layer2_neigh_add(interf->l2net, &l2neigh_mac);
165   if (!l2neigh) {
166     /* no memory for l2 neighbor */
167     return;
168   }
169
170   if (sinfo[NL80211_STA_INFO_INACTIVE_TIME]) {
171     l2neigh->last_seen = oonf_clock_get_absolute(-((int64_t)(nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME]))));
172   }
173
174   /* byte data is 64 bit */
175   if (sinfo[NL80211_STA_INFO_RX_BYTES64]) {
176     nl80211_change_l2neigh_data(l2neigh, OONF_LAYER2_NEIGH_RX_BYTES, nla_get_u64(sinfo[NL80211_STA_INFO_RX_BYTES64]));
177   }
178   if (sinfo[NL80211_STA_INFO_TX_BYTES64]) {
179     nl80211_change_l2neigh_data(l2neigh, OONF_LAYER2_NEIGH_TX_BYTES, nla_get_u64(sinfo[NL80211_STA_INFO_TX_BYTES64]));
180   }
181
182   /* packet data is only 32 bit */
183   if (sinfo[NL80211_STA_INFO_RX_PACKETS]) {
184     _handle_traffic(l2neigh, OONF_LAYER2_NEIGH_RX_FRAMES, nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]));
185   }
186   if (sinfo[NL80211_STA_INFO_TX_PACKETS]) {
187     _handle_traffic(l2neigh, OONF_LAYER2_NEIGH_TX_FRAMES, nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]));
188   }
189   if (sinfo[NL80211_STA_INFO_TX_RETRIES]) {
190     _handle_traffic(l2neigh, OONF_LAYER2_NEIGH_TX_RETRIES, nla_get_u32(sinfo[NL80211_STA_INFO_TX_RETRIES]));
191   }
192   if (sinfo[NL80211_STA_INFO_TX_FAILED]) {
193     _handle_traffic(l2neigh, OONF_LAYER2_NEIGH_TX_FAILED, nla_get_u32(sinfo[NL80211_STA_INFO_TX_FAILED]));
194   }
195
196   /* bitrates are special */
197   if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
198     int64_t rate = _get_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE]);
199     if (rate) {
200       nl80211_change_l2neigh_data(l2neigh, OONF_LAYER2_NEIGH_TX_BITRATE, rate);
201     }
202   }
203   if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
204     int64_t rate = _get_bitrate(sinfo[NL80211_STA_INFO_RX_BITRATE]);
205     if (rate) {
206       nl80211_change_l2neigh_data(l2neigh, OONF_LAYER2_NEIGH_RX_BITRATE, rate);
207     }
208   }
209
210   /* expected throughput is special too */
211   if (sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]) {
212     int64_t rate;
213
214     rate = nla_get_u32(sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]);
215
216     /* convert in bps */
217     nl80211_change_l2neigh_data(l2neigh, OONF_LAYER2_NEIGH_TX_THROUGHPUT, rate * 1024ll);
218   }
219
220   /* signal strength is special too */
221   if (sinfo[NL80211_STA_INFO_SIGNAL]) {
222     int8_t signal;
223
224     signal = nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
225     nl80211_change_l2neigh_data(l2neigh, OONF_LAYER2_NEIGH_RX_SIGNAL, signal * 1000ll);
226   }
227
228   /* remove old data */
229   nl80211_cleanup_l2neigh_data(l2neigh);
230
231   /* and commit the changes */
232   oonf_layer2_neigh_commit(l2neigh);
233 }
234
235 static bool
236 _handle_traffic(struct oonf_layer2_neigh *l2neigh, enum oonf_layer2_neighbor_index idx, uint32_t new_32bit) {
237   static const uint64_t UPPER_32_MASK = 0xffffffff00000000ull;
238   static const uint64_t LOWER_32_MASK = 0x00000000ffffffffull;
239   struct oonf_layer2_data *data;
240   int64_t old_value, new_value;
241
242   new_value = 0;
243   old_value = 0;
244
245   data = &l2neigh->data[idx];
246   oonf_layer2_data_read_int64(&old_value, data);
247
248   new_value = old_value & UPPER_32_MASK;
249   new_value |= (new_32bit & LOWER_32_MASK);
250
251   OONF_DEBUG(LOG_NL80211, "new32: 0x%08x old: %016" PRIx64 " new: %016" PRIx64, new_32bit, old_value, new_value);
252
253   if (new_value + 0x80000000ll < old_value) {
254     /* handle 32bit counter overflow */
255     new_value += 0x100000000ll;
256     OONF_DEBUG(LOG_NL80211, "Overflow, new: %016" PRIx64, new_value);
257   }
258
259   return nl80211_change_l2neigh_data(l2neigh, idx, new_value);
260 }
261
262 static int64_t
263 _get_bitrate(struct nlattr *bitrate_attr) {
264   int rate = 0;
265   struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
266   static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
267     [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
268     [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
269   };
270
271   if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, bitrate_attr, rate_policy)) {
272     /* error in parsing bitrate nested arguments */
273     return 0;
274   }
275
276   if (rinfo[NL80211_RATE_INFO_BITRATE32])
277     rate = nla_get_u32(rinfo[NL80211_RATE_INFO_BITRATE32]);
278   else if (rinfo[NL80211_RATE_INFO_BITRATE])
279     rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
280
281   return 1024ll * 1024ll * rate / 10ll;
282 }