source: src/router/libutils/mac80211autochannel.c @ 16655

Last change on this file since 16655 was 16655, checked in by chris, 2 years ago

mac80211 fix autochannel segfault

File size: 8.9 KB
Line 
1/*
2 * Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2.1
6 * as published by the Free Software Foundation
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * GNU General Public License for more details.
12 */
13#include <sys/types.h>
14#include <sys/socket.h>
15#include <net/if.h>
16#include <unistd.h>
17#include <stdio.h>
18#include <fcntl.h>
19#include <stdbool.h>
20
21#include "unl.h"
22#include "linux/nl80211.h"
23#include "list.h"
24#include "list_sort.h"
25
26#include "wlutils.h"
27
28#ifndef ARRAY_SIZE
29#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
30#endif
31
32static struct mac80211_ac *add_to_mac80211_ac(struct mac80211_ac *list_root);
33void free_mac80211_ac(struct mac80211_ac *acs);
34
35static const char *freq_range;
36
37struct frequency {
38        struct list_head list;
39        unsigned int freq;
40        bool passive;
41        int quality;
42        int clear;
43        int clear_count;
44        int noise;
45        int noise_count;
46};
47
48static LIST_HEAD(frequencies);
49
50static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
51        [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
52        [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
53        [NL80211_SURVEY_INFO_CHANNEL_TIME] = { .type = NLA_U64 },
54        [NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY] = { .type = NLA_U64 },
55};
56
57static const int bias_2g[] = { 100, 50, 75, 75, 50, 100, 50, 75, 75, 50, 100, 50, 75 };
58
59static bool in_range(unsigned long freq)
60{
61        const char *s = freq_range;
62        unsigned long start, stop;
63        char *end = NULL;
64
65        if (!freq_range)
66                return true;
67
68        while (s && *s) {
69                start = strtoul(s, &end, 10);
70                s = end;
71                switch (*s) {
72                case '-':
73                        stop = strtoul(s + 1, &end, 10);
74                        if (freq >= start && freq <= stop)
75                                return true;
76
77                        if (*end != ',')
78                                return false;
79
80                        s++;
81                        break;
82                case ',':
83                        s++;
84                        /* fall through */
85                case '\0':
86                        if (start == freq)
87                                return true;
88                        break;
89                }
90        }
91        return false;
92}
93
94static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
95        [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
96};
97
98static int freq_list(struct unl *unl, int phy)
99{
100        struct nlattr *tb[NL80211_BAND_ATTR_MAX + 1];
101        struct frequency *f;
102        struct nl_msg *msg;
103        struct nlattr *band, *bands, *freqlist, *freq;
104        int rem, rem2, freq_mhz;
105
106        msg = unl_genl_msg(unl, NL80211_CMD_GET_WIPHY, false);
107        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phy);
108        if (unl_genl_request_single(unl, msg, &msg) < 0)
109                return NL_SKIP;
110
111        bands = unl_find_attr(unl, msg, NL80211_ATTR_WIPHY_BANDS);
112        if (!bands)
113                goto out;
114
115        nla_for_each_nested(band, bands, rem) {
116                freqlist = nla_find(nla_data(band), nla_len(band),
117                                    NL80211_BAND_ATTR_FREQS);
118                if (!freqlist)
119                        continue;
120
121                nla_for_each_nested(freq, freqlist, rem2) {
122                        nla_parse_nested(tb, NL80211_FREQUENCY_ATTR_MAX,
123                                         freq, freq_policy);
124                        if (!tb[NL80211_FREQUENCY_ATTR_FREQ])
125                                continue;
126
127                        if (tb[NL80211_FREQUENCY_ATTR_DISABLED])
128                                continue;
129
130                        freq_mhz = nla_get_u32(tb[NL80211_FREQUENCY_ATTR_FREQ]);
131                        if (!in_range(freq_mhz))
132                                continue;
133
134                        f = calloc(1, sizeof(*f));
135                        INIT_LIST_HEAD(&f->list);
136
137                        f->freq = freq_mhz;
138                        list_add_tail(&f->list, &frequencies);
139                        if (tb[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN])
140                                f->passive = true;
141                }
142        }
143
144out:
145nla_put_failure:
146        nlmsg_free(msg);
147        return NL_SKIP;
148}
149
150
151static int parse_survey(struct nl_msg *msg, struct nlattr **sinfo)
152{
153        struct nlattr *tb[NL80211_ATTR_MAX + 1];
154        struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
155
156        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
157                  genlmsg_attrlen(gnlh, 0), NULL);
158
159        if (!tb[NL80211_ATTR_SURVEY_INFO])
160                return -1;
161
162        if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
163                             tb[NL80211_ATTR_SURVEY_INFO],
164                             survey_policy))
165                return -1;
166
167        if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
168                return -1;
169
170        return 0;
171}
172
173static struct frequency *get_freq(int freq)
174{
175        struct frequency *f;
176
177        list_for_each_entry(f, &frequencies, list) {
178                if (f->freq != freq)
179                        continue;
180
181                return f;
182        }
183        return NULL;
184}
185
186static int freq_add_stats(struct nl_msg *msg, void *data)
187{
188        struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
189        struct frequency *f;
190        int freq;
191
192        if (parse_survey(msg, sinfo))
193                goto out;
194
195        freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
196        f = get_freq(freq);
197        if (!f)
198                goto out;
199
200        if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME] &&
201            sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) {
202                uint64_t time, busy;
203
204                time = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]);
205                busy = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]);
206                if (!time)
207                        goto out;
208
209                f->clear += 100 - (uint32_t) (busy * 100 / time);
210                f->clear_count++;
211        }
212
213        if (sinfo[NL80211_SURVEY_INFO_NOISE]) {
214                int8_t noise = nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
215                f->noise += noise;
216                f->noise_count++;
217        }
218
219out:
220        return NL_SKIP;
221}
222
223static void survey(struct unl *unl, int wdev, unl_cb cb)
224{
225        struct nl_msg *msg;
226
227        msg = unl_genl_msg(unl, NL80211_CMD_GET_SURVEY, true);
228        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev);
229        unl_genl_request(unl, msg, cb, NULL);
230        return;
231
232nla_put_failure:
233        nlmsg_free(msg);
234}
235
236static int scan_event_cb(struct nl_msg *msg, void *data)
237{
238        struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
239        struct unl *unl = data;
240
241        switch (gnlh->cmd) {
242        case NL80211_CMD_NEW_SCAN_RESULTS:
243        case NL80211_CMD_SCAN_ABORTED:
244                unl_loop_done(unl);
245        default:
246                return NL_SKIP;
247        }
248}
249
250static void scan(struct unl *unl, int wdev)
251{
252        struct frequency *f;
253        struct nlattr *opts;
254        struct nl_msg *msg;
255        int i = 0;
256
257        msg = unl_genl_msg(unl, NL80211_CMD_TRIGGER_SCAN, false);
258        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev);
259
260        opts = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
261        list_for_each_entry(f, &frequencies, list) {
262                NLA_PUT_U32(msg, ++i, f->freq);
263        }
264        nla_nest_end(msg, opts);
265
266        unl_genl_subscribe(unl, "scan");
267        if ((i = unl_genl_request(unl, msg, NULL, NULL)) < 0) {
268                fprintf(stderr, "Scan request failed: %s\n", strerror(-i));
269                goto out;
270        }
271
272        unl_genl_loop(unl, scan_event_cb, unl);
273
274out:
275        unl_genl_unsubscribe(unl, "scan");
276        return;
277
278nla_put_failure:
279        nlmsg_free(msg);
280}
281
282struct sort_data {
283        int lowest_noise;
284};
285
286static int freq_quality(struct frequency *f, struct sort_data *s)
287{
288        int c;
289        int idx;
290
291        if (!f->clear_count)
292                return 0;
293
294        c = f->clear;
295
296        idx = (f->freq - 2412) / 5;
297
298        /* strongly discourage the use of channels other than 1,6,11 */
299        if (f->freq >= 2412 && idx < ARRAY_SIZE(bias_2g))
300                c = (c * bias_2g[idx]) / 100;
301
302        /* subtract 2 * the number of db that the noise value is over the
303         * lowest that was found to discourage noisy channels */
304        c -= 2 * (f->noise - s->lowest_noise);
305
306        if (c < 0)
307                c = 0;
308
309        return c;
310}
311
312static int sort_cmp(void *priv, struct list_head *a, struct list_head *b)
313{
314        struct frequency *f1 = container_of(a, struct frequency, list);
315        struct frequency *f2 = container_of(b, struct frequency, list);
316
317        if (f1->quality > f2->quality)
318                return -1;
319        else
320                return (f1->quality < f2->quality);
321}
322
323// leave space for enhencements with more cards and already chosen channels...
324struct mac80211_ac *mac80211autochannel(char *interface, char *freq_range, int scans, int ammount, int enable_passive) {
325        struct mac80211_ac *acs = NULL;
326        struct frequency *f;
327        struct unl unl;
328        int verbose = 0;
329        int i, ch;
330        struct sort_data sdata;
331        int wdev, phy;
332
333        unsigned int count = ammount;
334
335        if (scans=0) scans=2;
336
337        unl_genl_init(&unl, "nl80211");
338        wdev = if_nametoindex(interface);
339        if (wdev < 0) {
340                fprintf(stderr, "mac80211autochannel Interface not found\n");
341                goto out;
342        }
343
344        phy = unl_nl80211_wdev_to_phy(&unl, wdev);
345        if (phy < 0) {
346                fprintf(stderr, "mac80211autochannel PHY not found\n");
347                goto out;
348        }
349
350        freq_list(&unl, phy);
351        for (i = 0; i < scans; i++) {
352                scan(&unl, wdev);
353                survey(&unl, wdev, freq_add_stats);
354        }
355
356        memset(&sdata, 0, sizeof(sdata));
357        list_for_each_entry(f, &frequencies, list) {
358                if (f->clear_count) {
359                        f->clear /= f->clear_count;
360                        f->clear_count = 1;
361                }
362
363                if (f->noise_count) {
364                        f->noise /= f->noise_count;
365                        f->noise_count = 1;
366                        if (f->noise < sdata.lowest_noise)
367                                sdata.lowest_noise = f->noise;
368                }
369        }
370
371        list_for_each_entry(f, &frequencies, list)
372                f->quality = freq_quality(f, &sdata);
373
374        list_sort(&sdata, &frequencies, sort_cmp);
375
376        list_for_each_entry(f, &frequencies, list) {
377                if (f->passive && !enable_passive)
378                        continue;
379
380                if (count-- == 0)
381                        break;
382                acs=add_to_mac80211_ac(acs);
383                acs->freq=f->freq;
384                acs->quality=f->quality;
385                acs->noise=f->noise;
386        }
387
388out:
389        unl_free(&unl);
390        return acs;
391}
392
393// thats wrong order
394static struct mac80211_ac *add_to_mac80211_ac(struct mac80211_ac *list_root){
395                struct mac80211_ac *new = calloc(1, sizeof(struct mac80211_ac));
396                if (new == NULL) {
397                        fprintf(stderr, "mac80211_autochannel add_to_mac80211_ac: Out of memory!\n");
398                        return(NULL);
399                        }
400                else
401                        {
402                         new->next =  list_root;
403                         return(new);
404                        }
405                }
406
407void free_mac80211_ac(struct mac80211_ac *acs) {
408        while (acs) {
409                struct mac80211_ac *next = acs->next;
410                free(acs);
411                acs = next;
412                }
413        }
Note: See TracBrowser for help on using the repository browser.