2 * Copyright (c) 2008 Sam Leffler, Errno Consulting
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 static const char rcsid[] = "$FreeBSD$";
29 #include <sys/types.h>
30 #include <sys/errno.h>
44 #include "regdomain.h"
46 #include <net80211/_ieee80211.h>
52 struct regdomain *rd; /* current domain */
53 struct netband *netband; /* current netband */
54 struct freqband *freqband; /* current freqband */
55 struct country *country; /* current country */
56 netband_head *curband; /* current netband list */
58 struct sbuf *sbuf[MAXLEVEL];
65 enum { DOMAIN, COUNTRY, FREQBAND } type;
69 start_element(void *data, const char *name, const char **attr)
71 #define iseq(a,b) (strcasecmp(a,b) == 0)
73 const void *id, *ref, *mode;
77 if (++mt->level == MAXLEVEL) {
78 /* XXX force parser to abort */
81 mt->sbuf[mt->level] = sbuf_new_auto();
82 id = ref = mode = NULL;
83 for (i = 0; attr[i] != NULL; i += 2) {
84 if (iseq(attr[i], "id")) {
86 } else if (iseq(attr[i], "ref")) {
88 } else if (iseq(attr[i], "mode")) {
91 printf("%*.*s[%s = %s]\n", mt->level + 1,
92 mt->level + 1, "", attr[i], attr[i+1]);
94 if (iseq(name, "rd") && mt->rd == NULL) {
95 if (mt->country == NULL) {
96 mt->rd = calloc(1, sizeof(struct regdomain));
97 mt->rd->name = strdup(id);
99 LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next);
101 mt->country->rd = (void *)strdup(ref);
104 if (iseq(name, "defcc") && mt->rd != NULL) {
105 mt->rd->cc = (void *)strdup(ref);
108 if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) {
113 if (iseq(mode, "11b"))
114 mt->curband = &mt->rd->bands_11b;
115 else if (iseq(mode, "11g"))
116 mt->curband = &mt->rd->bands_11g;
117 else if (iseq(mode, "11a"))
118 mt->curband = &mt->rd->bands_11a;
119 else if (iseq(mode, "11ng"))
120 mt->curband = &mt->rd->bands_11ng;
121 else if (iseq(mode, "11na"))
122 mt->curband = &mt->rd->bands_11na;
123 /* XXX else complain */
126 if (iseq(name, "band") && mt->netband == NULL) {
127 if (mt->curband == NULL) {
131 mt->netband = calloc(1, sizeof(struct netband));
132 LIST_INSERT_HEAD(mt->curband, mt->netband, next);
135 if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) {
136 /* XXX handle inlines and merge into table? */
137 if (mt->netband->band != NULL) {
140 mt->netband->band = (void *)strdup(ref);
144 if (iseq(name, "country") && mt->country == NULL) {
145 mt->country = calloc(1, sizeof(struct country));
146 mt->country->isoname = strdup(id);
148 LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next);
152 if (iseq(name, "freqband") && mt->freqband == NULL) {
153 mt->freqband = calloc(1, sizeof(struct freqband));
154 mt->freqband->id = strdup(id);
156 LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next);
163 decode_flag(const char *p, int len)
165 #define iseq(a,b) (strcasecmp(a,b) == 0)
166 static const struct {
171 #define FLAG(x) { #x, sizeof(#x), x }
172 FLAG(IEEE80211_CHAN_A),
173 FLAG(IEEE80211_CHAN_B),
174 FLAG(IEEE80211_CHAN_G),
175 FLAG(IEEE80211_CHAN_HT20),
176 FLAG(IEEE80211_CHAN_HT40),
177 FLAG(IEEE80211_CHAN_ST),
178 FLAG(IEEE80211_CHAN_TURBO),
179 FLAG(IEEE80211_CHAN_PASSIVE),
180 FLAG(IEEE80211_CHAN_DFS),
181 FLAG(IEEE80211_CHAN_CCK),
182 FLAG(IEEE80211_CHAN_OFDM),
183 FLAG(IEEE80211_CHAN_2GHZ),
184 FLAG(IEEE80211_CHAN_5GHZ),
185 FLAG(IEEE80211_CHAN_DYN),
186 FLAG(IEEE80211_CHAN_GFSK),
187 FLAG(IEEE80211_CHAN_GSM),
188 FLAG(IEEE80211_CHAN_STURBO),
189 FLAG(IEEE80211_CHAN_HALF),
190 FLAG(IEEE80211_CHAN_QUARTER),
191 FLAG(IEEE80211_CHAN_HT40U),
192 FLAG(IEEE80211_CHAN_HT40D),
193 FLAG(IEEE80211_CHAN_4MSXMIT),
194 FLAG(IEEE80211_CHAN_NOADHOC),
195 FLAG(IEEE80211_CHAN_NOHOSTAP),
196 FLAG(IEEE80211_CHAN_11D),
197 FLAG(IEEE80211_CHAN_FHSS),
198 FLAG(IEEE80211_CHAN_PUREG),
199 FLAG(IEEE80211_CHAN_108A),
200 FLAG(IEEE80211_CHAN_108G),
205 for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++)
206 if (len == flags[i].len && iseq(p, flags[i].name))
207 return flags[i].value;
213 end_element(void *data, const char *name)
215 #define iseq(a,b) (strcasecmp(a,b) == 0)
221 sbuf_finish(mt->sbuf[mt->level]);
222 p = sbuf_data(mt->sbuf[mt->level]);
223 len = sbuf_len(mt->sbuf[mt->level]);
225 /* <freqband>...</freqband> */
226 if (iseq(name, "freqstart") && mt->freqband != NULL) {
227 mt->freqband->freqStart = strtoul(p, NULL, 0);
230 if (iseq(name, "freqend") && mt->freqband != NULL) {
231 mt->freqband->freqEnd = strtoul(p, NULL, 0);
234 if (iseq(name, "chanwidth") && mt->freqband != NULL) {
235 mt->freqband->chanWidth = strtoul(p, NULL, 0);
238 if (iseq(name, "chansep") && mt->freqband != NULL) {
239 mt->freqband->chanSep = strtoul(p, NULL, 0);
242 if (iseq(name, "flags")) {
243 if (mt->freqband != NULL)
244 mt->freqband->flags |= decode_flag(p, len);
245 else if (mt->netband != NULL)
246 mt->netband->flags |= decode_flag(p, len);
254 if (iseq(name, "name") && mt->rd != NULL) {
255 mt->rd->name = strdup(p);
258 if (iseq(name, "sku") && mt->rd != NULL) {
259 mt->rd->sku = strtoul(p, NULL, 0);
262 if (iseq(name, "netband") && mt->rd != NULL) {
267 /* <band> ... </band> */
268 if (iseq(name, "freqband") && mt->netband != NULL) {
269 /* XXX handle inline freqbands */
272 if (iseq(name, "maxpower") && mt->netband != NULL) {
273 mt->netband->maxPower = strtoul(p, NULL, 0);
276 if (iseq(name, "maxpowerdfs") && mt->netband != NULL) {
277 mt->netband->maxPowerDFS = strtoul(p, NULL, 0);
281 /* <country>...</country> */
282 if (iseq(name, "isocc") && mt->country != NULL) {
283 mt->country->code = strtoul(p, NULL, 0);
286 if (iseq(name, "name") && mt->country != NULL) {
287 mt->country->name = strdup(p);
292 printf("Unexpected XML: name \"%s\" data \"%s\"\n", name, p);
296 if (iseq(name, "freqband") && mt->freqband != NULL) {
297 /* XXX must have start/end frequencies */
298 /* XXX must have channel width/sep */
303 if (iseq(name, "rd") && mt->rd != NULL) {
308 if (iseq(name, "band") && mt->netband != NULL) {
309 if (mt->netband->band == NULL) {
310 printf("No frequency band information at line %d\n",
312 XML_GetCurrentLineNumber(parser));
317 if (mt->netband->maxPower == 0) {
320 /* default max power w/ DFS to max power */
321 if (mt->netband->maxPowerDFS == 0)
322 mt->netband->maxPowerDFS = mt->netband->maxPower;
327 if (iseq(name, "netband") && mt->netband != NULL) {
332 if (iseq(name, "country") && mt->country != NULL) {
333 if (mt->country->code == 0) {
334 /* XXX must have iso cc */
336 if (mt->country->name == NULL) {
337 /* XXX must have name */
339 if (mt->country->rd == NULL) {
346 sbuf_delete(mt->sbuf[mt->level]);
347 mt->sbuf[mt->level--] = NULL;
352 char_data(void *data, const XML_Char *s, int len)
361 for (; isspace(*b) && b < e; b++)
363 for (; isspace(*e) && e > b; e++)
365 if (e != b || (*b != '\0' && !isspace(*b)))
366 sbuf_bcat(mt->sbuf[mt->level], b, e-b+1);
370 findid(struct regdata *rdp, const void *id, int type)
374 for (ip = rdp->ident; ip->id != NULL; ip++)
375 if (ip->type == type && strcasecmp(ip->id, id) == 0)
381 * Parse an regdomain XML configuration and build the internal representation.
384 lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len)
388 struct regdomain *dp;
395 memset(rdp, 0, sizeof(struct regdata));
396 mt = calloc(1, sizeof(struct mystate));
399 /* parse the XML input */
401 parser = XML_ParserCreate(NULL);
402 XML_SetUserData(parser, mt);
403 XML_SetElementHandler(parser, start_element, end_element);
404 XML_SetCharacterDataHandler(parser, char_data);
405 if (XML_Parse(parser, p, len, 1) != XML_STATUS_OK) {
406 warnx("%s: %s at line %ld", __func__,
407 XML_ErrorString(XML_GetErrorCode(parser)),
408 XML_GetCurrentLineNumber(parser));
411 XML_ParserFree(parser);
413 /* setup the identifer table */
414 rdp->ident = calloc(sizeof(struct ident), mt->nident + 1);
415 if (rdp->ident == NULL)
421 LIST_FOREACH(dp, &rdp->domains, next) {
422 rdp->ident[i].id = dp->name;
423 rdp->ident[i].p = dp;
424 rdp->ident[i].type = DOMAIN;
427 LIST_FOREACH(fp, &rdp->freqbands, next) {
428 rdp->ident[i].id = fp->id;
429 rdp->ident[i].p = fp;
430 rdp->ident[i].type = FREQBAND;
433 LIST_FOREACH(cp, &rdp->countries, next) {
434 rdp->ident[i].id = cp->isoname;
435 rdp->ident[i].p = cp;
436 rdp->ident[i].type = COUNTRY;
440 /* patch references */
441 LIST_FOREACH(dp, &rdp->domains, next) {
442 if (dp->cc != NULL) {
444 dp->cc = findid(rdp, id, COUNTRY);
445 if (dp->cc == NULL) {
446 warnx("undefined country \"%s\"",
447 __DECONST(char *, id));
450 free(__DECONST(char *, id));
452 LIST_FOREACH(nb, &dp->bands_11b, next) {
453 id = findid(rdp, nb->band, FREQBAND);
455 warnx("undefined 11b band \"%s\"",
456 __DECONST(char *, nb->band));
461 LIST_FOREACH(nb, &dp->bands_11g, next) {
462 id = findid(rdp, nb->band, FREQBAND);
464 warnx("undefined 11g band \"%s\"",
465 __DECONST(char *, nb->band));
470 LIST_FOREACH(nb, &dp->bands_11a, next) {
471 id = findid(rdp, nb->band, FREQBAND);
473 warnx("undefined 11a band \"%s\"",
474 __DECONST(char *, nb->band));
479 LIST_FOREACH(nb, &dp->bands_11ng, next) {
480 id = findid(rdp, nb->band, FREQBAND);
482 warnx("undefined 11ng band \"%s\"",
483 __DECONST(char *, nb->band));
488 LIST_FOREACH(nb, &dp->bands_11na, next) {
489 id = findid(rdp, nb->band, FREQBAND);
491 warnx("undefined 11na band \"%s\"",
492 __DECONST(char *, nb->band));
498 LIST_FOREACH(cp, &rdp->countries, next) {
500 cp->rd = findid(rdp, id, DOMAIN);
501 if (cp->rd == NULL) {
502 warnx("undefined country \"%s\"",
503 __DECONST(char *, id));
506 free(__DECONST(char *, id));
509 return errors ? EINVAL : 0;
513 cleanup_bands(netband_head *head)
518 nb = LIST_FIRST(head);
526 * Cleanup state/resources for a previously parsed regdomain database.
529 lib80211_regdomain_cleanup(struct regdata *rdp)
535 struct regdomain *dp = LIST_FIRST(&rdp->domains);
538 LIST_REMOVE(dp, next);
539 cleanup_bands(&dp->bands_11b);
540 cleanup_bands(&dp->bands_11g);
541 cleanup_bands(&dp->bands_11a);
542 cleanup_bands(&dp->bands_11ng);
543 cleanup_bands(&dp->bands_11na);
544 if (dp->name != NULL)
545 free(__DECONST(char *, dp->name));
548 struct country *cp = LIST_FIRST(&rdp->countries);
551 LIST_REMOVE(cp, next);
552 if (cp->name != NULL)
553 free(__DECONST(char *, cp->name));
557 struct freqband *fp = LIST_FIRST(&rdp->freqbands);
560 LIST_REMOVE(fp, next);
566 lib80211_alloc_regdata(void)
573 rdp = calloc(1, sizeof(struct regdata));
575 fd = open(_PATH_REGDOMAIN, O_RDONLY);
578 warn("%s: open(%s)", __func__, _PATH_REGDOMAIN);
583 if (fstat(fd, &sb) < 0) {
585 warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN);
591 xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
592 if (xml == MAP_FAILED) {
594 warn("%s: mmap", __func__);
600 if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) {
602 warn("%s: error reading regulatory database", __func__);
604 munmap(xml, sb.st_size);
609 munmap(xml, sb.st_size);
616 lib80211_free_regdata(struct regdata *rdp)
618 lib80211_regdomain_cleanup(rdp);
623 * Lookup a regdomain by SKU.
625 const struct regdomain *
626 lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku)
628 const struct regdomain *dp;
630 LIST_FOREACH(dp, &rdp->domains, next) {
638 * Lookup a regdomain by name.
640 const struct regdomain *
641 lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name)
643 const struct regdomain *dp;
645 LIST_FOREACH(dp, &rdp->domains, next) {
646 if (strcasecmp(dp->name, name) == 0)
653 * Lookup a country by ISO country code.
655 const struct country *
656 lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc)
658 const struct country *cp;
660 LIST_FOREACH(cp, &rdp->countries, next) {
668 * Lookup a country by ISO/long name.
670 const struct country *
671 lib80211_country_findbyname(const struct regdata *rdp, const char *name)
673 const struct country *cp;
677 LIST_FOREACH(cp, &rdp->countries, next) {
678 if (strcasecmp(cp->isoname, name) == 0 ||
679 strncasecmp(cp->name, name, len) == 0)